diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index ddbe453081..0fabde5fae 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/app/src/main/java/com/tangem/cardReader/CardCrypto.java b/app/src/main/java/com/tangem/cardReader/CardCrypto.java index f5b8c95c73..72681ac5ba 100644 --- a/app/src/main/java/com/tangem/cardReader/CardCrypto.java +++ b/app/src/main/java/com/tangem/cardReader/CardCrypto.java @@ -1,238 +1,238 @@ -package com.tangem.cardReader; - -import android.util.Log; - -import org.spongycastle.asn1.ASN1EncodableVector; -import org.spongycastle.asn1.ASN1Integer; -import org.spongycastle.asn1.DERSequence; -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; -import org.spongycastle.jce.spec.ECPrivateKeySpec; -import org.spongycastle.jce.spec.ECPublicKeySpec; -import org.spongycastle.math.ec.ECPoint; - -import java.math.BigInteger; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.Signature; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * Created by dvol on 14.11.2017. - */ - -public class CardCrypto { - public static PublicKey LoadPublicKey(byte[] publicKeyArray) throws Exception { - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - KeyFactory factory = KeyFactory.getInstance("EC", "SC"); - - ECPoint p1 = spec.getCurve().decodePoint(publicKeyArray); - ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec); - - return factory.generatePublic(keySpec); - } - - public static boolean VerifySignature(byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception { - Signature signatureInstance = Signature.getInstance("SHA256withECDSA"); - PublicKey publicKey = LoadPublicKey(publicKeyArray); - signatureInstance.initVerify(publicKey); - signatureInstance.update(data); - - ASN1EncodableVector v = new ASN1EncodableVector(); - int size = signature.length / 2; - v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, 0, size)))); - v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, size, size * 2)))); - byte[] sigDer = new DERSequence(v).getEncoded(); - - return signatureInstance.verify(sigDer); - } - - -// public static boolean isCanonical(BigInteger s) { -// -// ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); -// -// BigInteger HALF_CURVE_ORDER = spec.getN().shiftRight(1); -// return s.compareTo(HALF_CURVE_ORDER) <= 0; -// } -// -// public static BigInteger toCanonicalised(BigInteger s) { -// -// // The order of the curve is the number of valid points that exist on that curve. If S is in the upper -// // half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that -// // N = 10 -// // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. -// // 10 - 8 == 2, giving us always the latter solution, which is canonical. -// ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); -// if(!isCanonical(s)) { -// BigInteger canon = spec.getN().subtract(s); -// //Log.e("TX_SIGN", "non Canonical S"); -// return canon; -// } -// -// return s; -// -// } -// -// public static BigInteger[] calcSign2(byte[] priv, byte[] hash) -// { -// ECDSASigner signer = new ECDSASigner(); -// BigInteger d = new BigInteger(priv); -// -// ECNamedCurveParameterSpec CURVE_PARAMS = ECNamedCurveTable.getParameterSpec("secp256k1"); -// ECDomainParameters CURVE = new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(), -// CURVE_PARAMS.getH()); -// ECPrivateKeyParameters params = new ECPrivateKeyParameters(d, CURVE); -// //ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub), CURVE); -// signer.init(true, params); -// BigInteger[] rs = signer.generateSignature(hash); -// return rs; -// -// } -// -// public static byte[] Signature3(byte[] privateKeyArray, byte[] data) throws Exception -// { -// byte[] hash=Util.calculateSHA256(data); -// BigInteger[] signBI=calcSign2(privateKeyArray, hash); -// //signBI[0]=toCanonicalised(signBI[0]); -// signBI[1]=toCanonicalised(signBI[1]); -// byte[] r = signBI[0].toByteArray(); -// byte[] s = signBI[1].toByteArray(); -// -// byte[] res = new byte[64]; -// if( r.length==32 ) { -// System.arraycopy(r, 0, res, 0, r.length); -// }else if( r.length==33 && r[0]==0 ){ -// System.arraycopy(r, 1, res, 0, r.length-1); -// }else { -// throw new Exception("unsupported r-length"); -// } -// if( s.length==32 ) { -// System.arraycopy(s, 0, res, 32, 32); -// }else{ -// throw new Exception("unsupported s-length"); -// } -// return res; -// } - - public static byte[] Signature(byte[] privateKeyArray, byte[] data) throws Exception { - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - KeyFactory factory = KeyFactory.getInstance("EC", "SC"); - - ECPrivateKeySpec keySpecP = new ECPrivateKeySpec(new BigInteger(1,privateKeyArray), spec); - - Signature signature = Signature.getInstance("SHA256withECDSA"); - - PrivateKey privateKey = factory.generatePrivate(keySpecP); - signature.initSign(privateKey); - signature.update(data); - byte[] enc = signature.sign(); - - if (enc[0] != 0x30) throw new Exception("bad encoding 1"); - if ((enc[1] & 0x80) != 0) throw new Exception("unsupported length encoding 1"); - if (enc[2] != 0x02) throw new Exception("bad encoding 2"); - if ((enc[3] & 0x80) != 0) throw new Exception("unsupported length encoding 2"); - 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"); - int sLength = enc[5 + rLength]; - - - int sPos = 6 + rLength; - byte[] res = new byte[64]; - if (rLength <= 32) { - System.arraycopy(enc, 4, res, 32-rLength, rLength); - rLength=32; - } else if (rLength == 33 && enc[4] == 0) { - rLength--; - System.arraycopy(enc, 5, res, 0, rLength); - } else { - Log.e("cardCrypto","r-length:" + String.valueOf(rLength)); - Log.e("cardCrypto","s-length:" + String.valueOf(sLength)); - Log.e("cardCrypto","enc:" +Util.bytesToHex(enc)); - throw new Exception("unsupported r-length - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)); - } - if (sLength <= 32) { - System.arraycopy(enc, sPos, res, rLength+32-sLength, sLength); - sLength=32; - } else if (sLength == 33 && enc[sPos] == 0) { - System.arraycopy(enc, sPos + 1, res, rLength, sLength - 1); - } else { - Log.e("cardCrypto","s-length:" + String.valueOf(sLength)); - Log.e("cardCrypto","r-length:" + String.valueOf(rLength)); - Log.e("cardCrypto","enc:" +Util.bytesToHex(enc)); - throw new Exception("unsupported s-length - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)); - } - - if(!VerifySignature(GeneratePublicKey(privateKeyArray), data, res)) - { - throw new Exception("Signature self verify failed - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)+",res:"+Util.bytesToHex(res)); - } - - return res; - } - - public static byte[] GeneratePublicKey(byte[] privateKeyArray) throws NoSuchProviderException, NoSuchAlgorithmException { - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - - - byte[] publicKeyArray = spec.getG().multiply(new BigInteger(1,privateKeyArray)).getEncoded(false); - - return publicKeyArray; - } - - /** - * Computes the PBKDF2 hash of a password. - * - * @param password the password to hash. - * @param salt the salt - * @param iterations the iteration count (slowness factor) - * @return the PBDKF2 hash of the password - */ - public static byte[] pbkdf2(byte[] password, byte[] salt, int iterations) - throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { - return PBKDF2.deriveKey(password, salt, iterations); - } - - public static byte[] Encrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException - { - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING"); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC"); - cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] mEncryptedData = cipher.doFinal(data); - return mEncryptedData; - } - - public static byte[] Decrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { - try { - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING"); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING"); - cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length )); - return decryptedData; - } - catch (Exception e) - { - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING"); - Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING"); - cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); - byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length )); - Log.e("decrypt",Util.bytesToHex(decryptedData)); - throw e; - } - } -} +package com.tangem.cardReader; + +import android.util.Log; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.jce.ECNamedCurveTable; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; +import org.spongycastle.math.ec.ECPoint; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * Created by dvol on 14.11.2017. + */ + +public class CardCrypto { + public static PublicKey LoadPublicKey(byte[] publicKeyArray) throws Exception { + ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); + KeyFactory factory = KeyFactory.getInstance("EC", "SC"); + + ECPoint p1 = spec.getCurve().decodePoint(publicKeyArray); + ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec); + + return factory.generatePublic(keySpec); + } + + public static boolean VerifySignature(byte[] publicKeyArray, byte[] data, byte[] signature) throws Exception { + Signature signatureInstance = Signature.getInstance("SHA256withECDSA"); + PublicKey publicKey = LoadPublicKey(publicKeyArray); + signatureInstance.initVerify(publicKey); + signatureInstance.update(data); + + ASN1EncodableVector v = new ASN1EncodableVector(); + int size = signature.length / 2; + v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, 0, size)))); + v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(signature, size, size * 2)))); + byte[] sigDer = new DERSequence(v).getEncoded(); + + return signatureInstance.verify(sigDer); + } + + +// public static boolean isCanonical(BigInteger s) { +// +// ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); +// +// BigInteger HALF_CURVE_ORDER = spec.getN().shiftRight(1); +// return s.compareTo(HALF_CURVE_ORDER) <= 0; +// } +// +// public static BigInteger toCanonicalised(BigInteger s) { +// +// // The order of the curve is the number of valid points that exist on that curve. If S is in the upper +// // half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that +// // N = 10 +// // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. +// // 10 - 8 == 2, giving us always the latter solution, which is canonical. +// ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); +// if(!isCanonical(s)) { +// BigInteger canon = spec.getN().subtract(s); +// //Log.e("TX_SIGN", "non Canonical S"); +// return canon; +// } +// +// return s; +// +// } +// +// public static BigInteger[] calcSign2(byte[] priv, byte[] hash) +// { +// ECDSASigner signer = new ECDSASigner(); +// BigInteger d = new BigInteger(priv); +// +// ECNamedCurveParameterSpec CURVE_PARAMS = ECNamedCurveTable.getParameterSpec("secp256k1"); +// ECDomainParameters CURVE = new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(), +// CURVE_PARAMS.getH()); +// ECPrivateKeyParameters params = new ECPrivateKeyParameters(d, CURVE); +// //ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub), CURVE); +// signer.init(true, params); +// BigInteger[] rs = signer.generateSignature(hash); +// return rs; +// +// } +// +// public static byte[] Signature3(byte[] privateKeyArray, byte[] data) throws Exception +// { +// byte[] hash=Util.calculateSHA256(data); +// BigInteger[] signBI=calcSign2(privateKeyArray, hash); +// //signBI[0]=toCanonicalised(signBI[0]); +// signBI[1]=toCanonicalised(signBI[1]); +// byte[] r = signBI[0].toByteArray(); +// byte[] s = signBI[1].toByteArray(); +// +// byte[] res = new byte[64]; +// if( r.length==32 ) { +// System.arraycopy(r, 0, res, 0, r.length); +// }else if( r.length==33 && r[0]==0 ){ +// System.arraycopy(r, 1, res, 0, r.length-1); +// }else { +// throw new Exception("unsupported r-length"); +// } +// if( s.length==32 ) { +// System.arraycopy(s, 0, res, 32, 32); +// }else{ +// throw new Exception("unsupported s-length"); +// } +// return res; +// } + + public static byte[] Signature(byte[] privateKeyArray, byte[] data) throws Exception { + ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); + KeyFactory factory = KeyFactory.getInstance("EC", "SC"); + + ECPrivateKeySpec keySpecP = new ECPrivateKeySpec(new BigInteger(1,privateKeyArray), spec); + + Signature signature = Signature.getInstance("SHA256withECDSA"); + + PrivateKey privateKey = factory.generatePrivate(keySpecP); + signature.initSign(privateKey); + signature.update(data); + byte[] enc = signature.sign(); + + if (enc[0] != 0x30) throw new Exception("bad encoding 1"); + if ((enc[1] & 0x80) != 0) throw new Exception("unsupported length encoding 1"); + if (enc[2] != 0x02) throw new Exception("bad encoding 2"); + if ((enc[3] & 0x80) != 0) throw new Exception("unsupported length encoding 2"); + 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"); + int sLength = enc[5 + rLength]; + + + int sPos = 6 + rLength; + byte[] res = new byte[64]; + if (rLength <= 32) { + System.arraycopy(enc, 4, res, 32-rLength, rLength); + rLength=32; + } else if (rLength == 33 && enc[4] == 0) { + rLength--; + System.arraycopy(enc, 5, res, 0, rLength); + } else { + Log.e("cardCrypto","r-length:" + String.valueOf(rLength)); + Log.e("cardCrypto","s-length:" + String.valueOf(sLength)); + Log.e("cardCrypto","enc:" +Util.bytesToHex(enc)); + throw new Exception("unsupported r-length - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)); + } + if (sLength <= 32) { + System.arraycopy(enc, sPos, res, rLength+32-sLength, sLength); + sLength=32; + } else if (sLength == 33 && enc[sPos] == 0) { + System.arraycopy(enc, sPos + 1, res, rLength, sLength - 1); + } else { + Log.e("cardCrypto","s-length:" + String.valueOf(sLength)); + Log.e("cardCrypto","r-length:" + String.valueOf(rLength)); + Log.e("cardCrypto","enc:" +Util.bytesToHex(enc)); + throw new Exception("unsupported s-length - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)); + } + + if(!VerifySignature(GeneratePublicKey(privateKeyArray), data, res)) + { + throw new Exception("Signature self verify failed - r-length:" + String.valueOf(rLength)+",s-length:" + String.valueOf(sLength)+",enc:" +Util.bytesToHex(enc)+",res:"+Util.bytesToHex(res)); + } + + return res; + } + + public static byte[] GeneratePublicKey(byte[] privateKeyArray) throws NoSuchProviderException, NoSuchAlgorithmException { + ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); + + + byte[] publicKeyArray = spec.getG().multiply(new BigInteger(1,privateKeyArray)).getEncoded(false); + + return publicKeyArray; + } + + /** + * Computes the PBKDF2 hash of a password. + * + * @param password the password to hash. + * @param salt the salt + * @param iterations the iteration count (slowness factor) + * @return the PBDKF2 hash of the password + */ + public static byte[] pbkdf2(byte[] password, byte[] salt, int iterations) + throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { + return PBKDF2.deriveKey(password, salt, iterations); + } + + public static byte[] Encrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException + { + SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING"); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING", "BC"); + cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); + byte[] mEncryptedData = cipher.doFinal(data); + return mEncryptedData; + } + + public static byte[] Decrypt(byte[] key, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + try { + SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS7PADDING"); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); + byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length )); + return decryptedData; + } + catch (Exception e) + { + SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/NOPADDING"); + Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16])); + byte[] decryptedData = cipher.doFinal(Arrays.copyOfRange(data, 0, data.length )); + Log.e("decrypt",Util.bytesToHex(decryptedData)); + throw e; + } + } +} diff --git a/app/src/main/java/com/tangem/cardReader/CardProtocol.java b/app/src/main/java/com/tangem/cardReader/CardProtocol.java index 3e0a9f1062..9943bbe653 100644 --- a/app/src/main/java/com/tangem/cardReader/CardProtocol.java +++ b/app/src/main/java/com/tangem/cardReader/CardProtocol.java @@ -1,954 +1,954 @@ -package com.tangem.cardReader; - -import android.content.Context; -import android.nfc.Tag; -import android.nfc.TagLostException; -import android.nfc.tech.IsoDep; -import android.util.Log; - -import com.tangem.wallet.CoinEngine; -import com.tangem.wallet.CoinEngineFactory; -import com.tangem.wallet.Issuer; -import com.tangem.wallet.Tangem_Card; -import com.tangem.wallet.Manufacturer; - -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.interfaces.ECPublicKey; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; -import org.spongycastle.math.ec.ECPoint; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.security.Security; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.InvalidKeySpecException; -import java.util.Calendar; - -import javax.crypto.KeyAgreement; - - -/** - * Created by dvol on 14.07.2017. - */ - -public class CardProtocol { - - - public interface Notifications { - void OnReadStart(CardProtocol cardProtocol); - - void OnReadProgress(CardProtocol cardProtocol, int progress); - - void OnReadFinish(CardProtocol cardProtocol); - - void OnReadCancel(); - - void OnReadWait(int msec); - - void OnReadBeforeRequest(int timeout); - - void OnReadAfterRequest(); - } - - - public void setError(Exception error) { - mError = error; - } - - public Tangem_Card getCard() { - return mCard; - } - - public Exception getError() { - return mError; - } - - private static final String logTag = "CardProtocol"; - public static final int SW_PIN_ERROR = SW.INVALID_PARAMS; - - public static final String DefaultPIN = "000000"; - public static final String DefaultPIN2 = "000"; - - protected IsoDep mIsoDep; - - public Tag getTag() { - return mIsoDep.getTag(); - } - - protected String mPIN; - protected Notifications mNotifications; - - public void setPIN(String PIN) { - mPIN = PIN; - if (mCard != null) { - mCard.setPIN(PIN); - } - } - - protected Tangem_Card mCard; - protected Exception mError; - protected Context mContext; - - static { - Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); - } - - public CardProtocol(Context context, IsoDep isoDep, Notifications notifications) { - mContext = context; - mIsoDep = isoDep; - mNotifications = notifications; - mPIN = null; - mCard = new Tangem_Card(Util.byteArrayToHexString(mIsoDep.getTag().getId())); - } - - public CardProtocol(Context context, IsoDep isoDep, Tangem_Card card, Notifications notifications) { - mContext = context; - mIsoDep = isoDep; - mNotifications = notifications; - mPIN = card.getPIN(); - mCard = card; - } - - public static class TangemException extends Exception { - public TangemException(String message) { - super(message); - } - } - - public static class TangemException_InvalidPIN extends TangemException { - public TangemException_InvalidPIN(String message) { - super(message); - } - } - - public static class TangemException_NeedPause extends TangemException { - public TangemException_NeedPause(String message) { - super(message); - } - } - - public static class TangemException_ExtendedLengthNotSupported extends TangemException { - public TangemException_ExtendedLengthNotSupported(String message) { - super(message); - } - } - - - public byte[] GetUID() { - if (mIsoDep == null || mIsoDep.getTag() == null) return null; - return mIsoDep.getTag().getId(); - } - - public int getTimeout() { - if (mIsoDep == null || mIsoDep.getTag() == null) return 60000; - return mIsoDep.getTimeout(); - } - - - protected byte[] protocolKey; - - public void resetProtocolKey() { - protocolKey = null; - } - - public void CreateProtocolKey() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { - protocolKey = CardCrypto.pbkdf2(Util.calculateSHA256(mPIN), mIsoDep.getTag().getId(), 50); - //Log.e("Reader", String.format("PIN: %s, Protocol key: %s", mPIN, Util.bytesToHex(protocolKey))); - if (sessionKey != null) { - sessionKey = null; - } - } - - byte[] sessionKey = null; - - public void run_OpenSession(Tangem_Card.EncryptionMode encryptionMode) throws Exception { - sessionKey = null; - try { - CommandApdu cmdApdu = new CommandApdu(CommandApdu.ISO_CLA, INS.OpenSession.Code, 0, encryptionMode.getP()); - - switch (encryptionMode) { - case Fast: { - byte[] baMyChallenge = Util.generateRandomBytes(16); - - cmdApdu.addTLV(TLV.Tag.TAG_Session_Key_A, baMyChallenge); - Log.i(logTag, cmdApdu.getCommandName()); - ResponseApdu rspApdu = null; - - try { - if (mIsoDep == null) { - throw new TagLostException(); - } - byte[] cmdBytes = cmdApdu.toBytes(); - String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc()); - Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr)); - - byte[] rsp = mIsoDep.transceive(cmdBytes); - rspApdu = new ResponseApdu(rsp); - - Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp))); - - if (rspApdu.isParsedWithError()) { - throw new Exception("Can't parse answer"); - } - } catch (Exception E) { - sessionKey = null; - throw E; - } - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - byte[] baTheirsChallenge = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Session_Key_B).Value; - if (protocolKey == null) CreateProtocolKey(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - outputStream.write(baMyChallenge); - outputStream.write(baTheirsChallenge); - outputStream.write(protocolKey); - sessionKey = Util.calculateSHA256(outputStream.toByteArray()); - //Log.i(logTag, String.format("Session key: %s", Util.bytesToHex(sessionKey))); - } else { - Log.e(logTag, String.format("Failed: %04X - %s", rspApdu.getSW1SW2(), rspApdu.getSW1SW2Description())); - throw new Exception(String.format("Can't open session: SW - %04X", rspApdu.getSW1SW2())); - } - } - break; - case Strong: { - KeyPairGenerator kpgen = KeyPairGenerator.getInstance("ECDH", "SC"); - kpgen.initialize(new ECGenParameterSpec("secp256k1"), new SecureRandom()); - KeyPair KP = kpgen.generateKeyPair(); - KeyAgreement ka = KeyAgreement.getInstance("ECDH", "SC"); - ka.init(KP.getPrivate()); - - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - //return spec.getG().multiply(new BigInteger((ECPrivateKey) )).getEncoded(false); - ECPublicKey eckey = (ECPublicKey) KP.getPublic(); - byte[] baMyPublicKey = eckey.getQ().getEncoded(false); - - cmdApdu.addTLV(TLV.Tag.TAG_Session_Key_A, baMyPublicKey); - Log.i(logTag, cmdApdu.getCommandName()); - ResponseApdu rspApdu = null; - - try { - if (mIsoDep == null) { - throw new TagLostException(); - } - //mIsoDep.setTimeout(msTimeout); - - byte[] cmdBytes = cmdApdu.toBytes(); - String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc()); - Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr)); - byte[] rsp = mIsoDep.transceive(cmdBytes); - rspApdu = new ResponseApdu(rsp); - Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp))); - - if (rspApdu.isParsedWithError()) { - throw new Exception("Can't parse answer"); - } - } catch (Exception E) { - sessionKey = null; - throw E; - } - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - byte[] baTheirsPublicKey = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Session_Key_B).Value; - ka.doPhase(CardCrypto.LoadPublicKey(baTheirsPublicKey), true); - if (protocolKey == null) CreateProtocolKey(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - outputStream.write(ka.generateSecret()); - outputStream.write(protocolKey); - sessionKey = Util.calculateSHA256(outputStream.toByteArray()); -// Log.i(logTag, String.format("Session key: %s", Util.bytesToHex(sessionKey))); - } else { - Log.i(logTag, String.format("Failed: %04X - %s", rspApdu.getSW1SW2(), rspApdu.getSW1SW2Description())); - throw new Exception(String.format("Can't open session: SW - %04X", rspApdu.getSW1SW2())); - } - } - break; - default: - throw new Exception("Unknown encryption mode"); - } - } catch (Exception e) { - e.printStackTrace(); - Log.e(logTag, String.format("Exception: %s", e.getMessage())); - throw new Exception("Can't open session: " + e.getMessage()); - } - } - - - // send command APDU, get response APDU, and display HEX data to user - private ResponseApdu SendAndReceive(CommandApdu cmdApdu, boolean breakOnNeedPause) throws Exception { - if (mCard.encryptionMode != Tangem_Card.EncryptionMode.None) { - if (sessionKey == null) { - run_OpenSession(mCard.encryptionMode); - } - cmdApdu.Crypt(sessionKey); - } - cmdApdu.setP1(mCard.encryptionMode.getP()); - byte[] cmdBytes = cmdApdu.toBytes(); - String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc()); - Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr)); - byte[] rsp; - ResponseApdu rspApdu; - try { - do { - try { - mNotifications.OnReadBeforeRequest(mIsoDep.getTimeout()); - try { - rsp = mIsoDep.transceive(cmdBytes); - } finally { - mNotifications.OnReadAfterRequest(); - } - } catch (IOException e) { - if (e.getMessage().contains("length exceeds supported maximum") && mIsoDep.isExtendedLengthApduSupported()) { - throw new TangemException_ExtendedLengthNotSupported(e.getMessage()); - } - throw e; - } - if (mCard.encryptionMode != Tangem_Card.EncryptionMode.None) { - rspApdu = ResponseApdu.Decrypt(rsp, sessionKey); - } else { - rspApdu = new ResponseApdu(rsp); - } - - if (rspApdu.isParsedWithError()) { - Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp))); - throw new TangemException(rspApdu.getParseErroMessage()); - } else if (rspApdu.getSW1SW2() == SW.NEED_PAUSE && mNotifications != null) { - int remainingPause = rspApdu.getTLVs().getTagAsInt(TLV.Tag.TAG_Pause) * 10; - Log.v("NFC", String.format(">> Security delay, remaining %f s", remainingPause / 1000.0)); - if (breakOnNeedPause) { - break; - } else { - mNotifications.OnReadWait(remainingPause); - } - } else { - Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp))); - } - } while (rspApdu.getSW1SW2() == SW.NEED_PAUSE); - } finally { - mNotifications.OnReadWait(0); - } - - return rspApdu; - } - - private CommandApdu StartPrepareCommand(INS ins) throws NoSuchAlgorithmException { - CommandApdu Apdu = new CommandApdu(ins); - byte[] baPIN = Util.calculateSHA256(mPIN); - Apdu.addTLV(TLV.Tag.TAG_PIN, baPIN); - if (ins != INS.Read) { - Apdu.addTLV(TLV.Tag.TAG_CardID, mCard.getCID()); - } - return Apdu; - } - - public void run_Read() throws Exception { - CommandApdu rqApdu = StartPrepareCommand(INS.Read); - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - - - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - - - readResult = rspApdu.getTLVs(); - - parseReadResult(); - - run_ReadOrWriteIssuerDataAndDefineOfflineBalance(); - - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible PIN is invalid!\n", rspApdu.getSW1SW2())); - } else { - throw new TangemException(String.format("FAILED: [%04X]\n", rspApdu.getSW1SW2())); - } - } - - public void run_ReadOrWriteIssuerDataAndDefineOfflineBalance() throws Exception { - TLVList tlvIssuerData; - if (mCard.getNeedWriteIssuerData()) { - run_WriteIssuerData(mCard.getIssuerData(), mCard.getIssuerDataSignature()); - try { - tlvIssuerData = TLVList.fromBytes(mCard.getIssuerData()); - } catch (TLVException e) { - e.printStackTrace(); - tlvIssuerData = null; - } - } else { - try { - tlvIssuerData = run_ReadIssuerData(); - } catch (Exception e) { - e.printStackTrace(); - tlvIssuerData = null; - } - } - if (mCard.getStatus() == Tangem_Card.Status.Loaded) { - // try read offline balance data - if (tlvIssuerData != null && tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination) != null && mCard.getMaxSignatures() == mCard.getRemainingSignatures()) { - mCard.setOfflineBalance(tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination).Value); - } else { - mCard.clearOfflineBalance(); - } - } else { - mCard.clearOfflineBalance(); - } - } - - private TLVList readResult = null; - - public void clearReadResult() { - sessionKey = null; - readResult = null; - } - - public boolean haveReadResult() { - return readResult != null; - } - - public void parseReadResult() throws TangemException, NoSuchProviderException, NoSuchAlgorithmException { - TLV tlvStatus = readResult.getTLV(TLV.Tag.TAG_Status); - mCard.setStatus(Tangem_Card.Status.fromCode(Util.byteArrayToInt(tlvStatus.Value))); - TLV tlvCID = readResult.getTLV(TLV.Tag.TAG_CardID); - mCard.setCID(tlvCID.Value); - mCard.setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString()), true); - mCard.setHealth(readResult.getTLV(TLV.Tag.TAG_Health).getAsInt()); - - if (mCard.getStatus() != Tangem_Card.Status.NotPersonalized) { - try { - TLV tlvCardPubkicKey = readResult.getTLV(TLV.Tag.TAG_CardPublicKey); - if (tlvCardPubkicKey == null) - throw new TangemException("Invalid answer format"); - mCard.setCardPublicKey(tlvCardPubkicKey.Value); - - TLVList tlvCardData = TLVList.fromBytes(readResult.getTLV(TLV.Tag.TAG_CardData).Value); - - TLV tokenSymbol = tlvCardData.getTLV(TLV.Tag.TAG_Token_Symbol); - TLV contractAddress = tlvCardData.getTLV(TLV.Tag.TAG_Token_Contract_Address); - TLV tokens_decimal = tlvCardData.getTLV(TLV.Tag.TAG_Token_Decimal); - - if (tokenSymbol != null) - mCard.setTokenSymbol(tokenSymbol.getAsString()); - - if (contractAddress != null) - mCard.setContractAddress(contractAddress.getAsString()); - - if (tokens_decimal != null) - mCard.setTokensDecimal(tokens_decimal.getAsInt()); - - // this method has reflection TokenSymbol - // you mast call setBlockchainIDFromCard after calling setTokensDecimal - mCard.setBlockchainIDFromCard(tlvCardData.getTLV(TLV.Tag.TAG_Blockchain_ID).getAsString()); - byte[] tlvPersonalizationDT = tlvCardData.getTLV(TLV.Tag.TAG_ManufactureDateTime).Value; - int year = (tlvPersonalizationDT[0] & 0xFF) << 8 | (tlvPersonalizationDT[1] & 0xFF); - int month = tlvPersonalizationDT[2] - 1; - int day = tlvPersonalizationDT[3]; - Calendar cd = Calendar.getInstance(); - cd.set(year, month, day, 0, 0, 0); - mCard.setPersonalizationDateTime(cd.getTime()); - try { - if (readResult.getTLV(TLV.Tag.TAG_Firmware) != null) { - mCard.setFirmwareVersion(readResult.getTLV(TLV.Tag.TAG_Firmware).getAsString()); - } else { - mCard.setFirmwareVersion(tlvCardData.getTLV(TLV.Tag.TAG_Firmware).getAsString()); - } - } catch (Exception e) { - e.printStackTrace(); - Log.e(logTag, "Cannot get firmware version"); - mCard.setFirmwareVersion("0.00"); - } - - try { - if (mCard.getFirmwareVersion().compareTo("1.05") < 0) { - mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value)); - } else { - mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Data_PublicKey).Value)); - } - } catch (Exception e) { - e.printStackTrace(); - Log.e(logTag, "Cannot get issuer, try a version for older cards"); - try { - mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value)); - } catch (Exception ee) { - ee.printStackTrace(); - Log.e(logTag, "Cannot get issuer"); - mCard.setIssuer(Issuer.Unknown); - } - } - - try { - mCard.setSettingsMask(readResult.getTagAsInt(TLV.Tag.TAG_SettingsMask)); - } catch (Exception e) { - e.printStackTrace(); - Log.e(logTag, "Can't get settings mask"); - } - - if (readResult.getTLV(TLV.Tag.TAG_PauseBeforePIN2) != null) { - mCard.setPauseBeforePIN2(10 * readResult.getTagAsInt(TLV.Tag.TAG_PauseBeforePIN2)); - } - - try { - mCard.setSigningMethod(readResult.getTagAsInt(TLV.Tag.TAG_SigningMethod)); - } catch (Exception e) { - e.printStackTrace(); - Log.e(logTag, "Can't get signing method"); - mCard.setSigningMethod(0); - } - - try { - mCard.setMaxSignatures(readResult.getTagAsInt(TLV.Tag.TAG_MaxSignatures)); - } catch (Exception e) { - e.printStackTrace(); - Log.e(logTag, "Can't get max signatures"); - } - - } catch (Exception e) { - e.printStackTrace(); - throw new TangemException("Can't parse card data"); - } - } - - - if (mCard.getStatus() == Tangem_Card.Status.Loaded) { - - TLV tlvPublicKey = readResult.getTLV(TLV.Tag.TAG_Wallet_PublicKey); - - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - ECPoint p1 = spec.getCurve().decodePoint(tlvPublicKey.Value); - - byte pkUncompressed[] = p1.getEncoded(false); - - byte pkCompresses[] = p1.getEncoded(true); - mCard.setWalletPublicKey(pkUncompressed); - mCard.setWalletPublicKeyRar(pkCompresses); - - CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain()); - String wallet = engineCoin.calculateAddress(mCard, pkUncompressed); - mCard.setWallet(wallet); - //mCard.setWallet(Blockchain.calculateWalletAddress(mCard, pkUncompressed)); - - mCard.setRemainingSignatures(readResult.getTagAsInt(TLV.Tag.TAG_RemainingSignatures)); - - } else { - mCard.setWallet("N/A"); - } - } - - - public TLVList run_VerifyCard() throws Exception { - if (mCard.getCardPublicKey() == null || readResult == null) { - run_Read(); - } - if (mCard.getStatus() == Tangem_Card.Status.NotPersonalized) { - getCard().setManufacturer(Manufacturer.Unknown, false); - return null; - } - - if (mCard.getCardPublicKey() == null) { - throw new TangemException("Not all data read, can't verify card!"); - } - - CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCard); - byte[] bChallenge = Util.generateRandomBytes(16); - rqApdu.addTLV(TLV.Tag.TAG_Challenge, bChallenge); - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - TLVList verifyResult = rspApdu.getTLVs(); - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - verifyResult.add(new TLV(TLV.Tag.TAG_Challenge, bChallenge)); - - TLV tlvSalt = verifyResult.getTLV(TLV.Tag.TAG_Salt); - TLV tlvCardSignature = verifyResult.getTLV(TLV.Tag.TAG_CardSignature); -// TLV tlvManufacturerSignature = verifyResult.getTLV(TLV.Tag.TAG_Manufacturer_Signature); - - if (tlvSalt == null || tlvCardSignature == null) { - throw new TangemException("Not all data read, can't verify card!"); - } - - try { - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - bs.write(bChallenge); - bs.write(tlvSalt.Value); - byte[] dataArray = bs.toByteArray(); - if (CardCrypto.VerifySignature(mCard.getCardPublicKey(), dataArray, tlvCardSignature.Value)) { - getCard().setCardPublicKeyValid(true); - Log.i(logTag, "Card signature verification OK"); - } else { - Log.e(logTag, "Card signature verification FAILED"); - getCard().setCardPublicKeyValid(false); - } - - //getCard().setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString(), bChallenge, tlvSalt.Value, tlvManufacturerSignature.Value), true); - getCard().setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString()), true); - } catch (Exception e) { - e.printStackTrace(); - getCard().setManufacturer(Manufacturer.Unknown, false); - } - return verifyResult; - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); - } else { - getCard().setManufacturer(Manufacturer.Unknown, false); - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - - } - - public void run_CreateWallet(String PIN2) throws Exception { - if (readResult == null) run_Read(); - 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(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(true); - } - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(false); - } - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - public TLVList run_CheckWallet() throws Exception { - CommandApdu rqApdu = StartPrepareCommand(INS.CheckWallet); - byte[] bChallenge = Util.generateRandomBytes(16); - rqApdu.addTLV(TLV.Tag.TAG_Challenge, bChallenge); - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - TLVList Result = rspApdu.getTLVs(); - Result.add(new TLV(TLV.Tag.TAG_Challenge, bChallenge)); - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - return Result; - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - public void run_CheckWalletWithSignatureVerify() throws Exception { - if (mCard.getCardPublicKey() == null || readResult == null) { - run_Read(); - } - if (mCard.getStatus() == Tangem_Card.Status.NotPersonalized) { - getCard().setManufacturer(Manufacturer.Unknown, false); - return; - } - - if (readResult.getTagAsInt(TLV.Tag.TAG_Status) != Tangem_Card.Status.Loaded.getCode()) { - throw new TangemException("Card must be loaded"); - } - TLVList checkResult = run_CheckWallet(); - if (checkResult == null) return; - - TLV tlvPublicKey = readResult.getTLV(TLV.Tag.TAG_Wallet_PublicKey); - TLV tlvChallenge = checkResult.getTLV(TLV.Tag.TAG_Challenge); - TLV tlvSalt = checkResult.getTLV(TLV.Tag.TAG_Salt); - TLV tlvSignature = checkResult.getTLV(TLV.Tag.TAG_Signature); - - if (tlvPublicKey == null || tlvChallenge == null || tlvSalt == null || tlvSignature == null) { - throw new TangemException("Not all data read, can't check signature!"); - } - - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - bs.write(tlvChallenge.Value); - bs.write(tlvSalt.Value); - byte[] dataArray = bs.toByteArray(); - - if (CardCrypto.VerifySignature(tlvPublicKey.Value, dataArray, tlvSignature.Value)) { - Log.i(logTag, "Signature verification OK"); - mCard.setWalletPublicKeyValid(true); - } else { - mCard.setWalletPublicKeyValid(false); - } - } - - public void run_PurgeWallet(String PIN2) throws Exception { - CommandApdu rqApdu = StartPrepareCommand(INS.PurgeWallet); - rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2)); - - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(true); - } - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(false); - } - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); - } else { - - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - public void run_SwapPIN(String PIN2, String newPin, String newPin2, boolean breakOnNeedPause) throws Exception { - CommandApdu rqApdu = StartPrepareCommand(INS.SwapPIN); - rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2)); - rqApdu.addTLV(TLV.Tag.TAG_NewPIN, Util.calculateSHA256(newPin)); - rqApdu.addTLV(TLV.Tag.TAG_NewPIN2, Util.calculateSHA256(newPin2)); - - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, breakOnNeedPause); - - if (rspApdu.isStatus(SW.PIN1_CHANGED) || rspApdu.isStatus(SW.PIN2_CHANGED) || rspApdu.isStatus(SW.PINS_CHANGED) || rspApdu.isStatus(SW.PINS_NOT_CHANGED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - if (newPin2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(true); - } else { - mCard.setUseDefaultPIN2(false); - } - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(false); - } - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); - } else if (breakOnNeedPause && rspApdu.isStatus(SW.NEED_PAUSE)) { - throw new TangemException_NeedPause(String.format("FAILED: [%04X] - Need pause\n", rspApdu.getSW1SW2())); - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - public void run_CheckPIN2isDefault() throws Exception { - if (mCard.isFirmwareNewer("1.19") || (mCard.isFirmwareNewer("1.12") && (mCard.getPauseBeforePIN2() == 0 || mCard.useSmartSecurityDelay()))) { - // can obtain SwapPIN(to default) answer without security delay - try check if PIN2 is default with card request - try { - run_SwapPIN(DefaultPIN2, mPIN, DefaultPIN2, true); - mCard.setUseDefaultPIN2(true); - } catch (TangemException_NeedPause e) { - mCard.setUseDefaultPIN2(null); - } catch (TangemException_InvalidPIN e) { - mCard.setUseDefaultPIN2(false); - } - } else { - mCard.setUseDefaultPIN2(null); - } - } - - public TLVList run_SignHashes(String PIN2, byte[][] hashes, boolean UseIssuerValidation, byte[] issuerData, Issuer issuer) throws Exception { - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - if (hashes.length > 10) throw new Exception("To much hashes in one transaction!"); - for (int i = 0; i < hashes.length; i++) { - if (i != 0 && hashes[0].length != hashes[i].length) - throw new Exception("Hashes length must be identical!"); - bs.write(hashes[i]); - } - 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()); - if (UseIssuerValidation) { - byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), bs.toByteArray()); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerSignature); - } - if (issuerData != null) { - if (issuer == null || issuer == Issuer.Unknown) - throw new Exception("Need known Issuer to write issuer Data"); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData); - byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), issuerData); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerSignature); - } - - - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - TLVList Result = rspApdu.getTLVs(); - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(true); - } - return Result; - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(false); - } - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2())); - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - public TLVList run_SignRaw(String PIN2, byte[] bTxOutData) throws Exception { - - CommandApdu rqApdu = StartPrepareCommand(INS.Sign); - rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2)); - rqApdu.addTLV(TLV.Tag.TAG_TrOut_Raw, bTxOutData); - rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, "sha-256x2".getBytes("US-ASCII")); - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - TLVList Result = rspApdu.getTLVs(); - Result.add(new TLV(TLV.Tag.TAG_TrOut_Raw, bTxOutData)); - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(true); - } - return Result; - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(false); - } - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2())); - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - private byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception { - if (readResult == null) run_Read(); - CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCode); - rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, hashAlgID.getBytes("US-ASCII")); - rqApdu.addTLV_U32(TLV.Tag.TAG_CodePageAddress, codePageAddress); - rqApdu.addTLV_U16(TLV.Tag.TAG_CodePageCount, codePageCount); - rqApdu.addTLV(TLV.Tag.TAG_Challenge, challenge); - - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - return rspApdu.getTLVs().getTLV(TLV.Tag.TAG_CodeHash).Value; - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - private void run_ValidateCard(String PIN2) throws Exception { - if (readResult == null) run_Read(); - CommandApdu rqApdu = StartPrepareCommand(INS.ValidateCard); - rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2)); - - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(true); - } - } else if (rspApdu.isStatus(SW_PIN_ERROR)) { - if (PIN2.equals(DefaultPIN2)) { - mCard.setUseDefaultPIN2(false); - } - throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2())); - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - private void run_WriteIssuerData(byte[] issuerData, byte[] issuerSignature) throws Exception { - run_Read(); - - CommandApdu rqApdu = StartPrepareCommand(INS.WriteIssuerData); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData); - rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerSignature); - - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - private TLVList run_ReadIssuerData() throws Exception { - CommandApdu rqApdu = StartPrepareCommand(INS.GetIssuerData); - - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - TLV issuerData = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data); - TLV issuerDataSignature = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data_Signature); - - if (issuerData == null || issuerDataSignature == null) - throw new TangemException("Invalid answer format (GetIssuerData)"); - - ByteArrayOutputStream bsDataToVerify = new ByteArrayOutputStream(); - bsDataToVerify.write(mCard.getCID()); - bsDataToVerify.write(issuerData.Value); - try { - if (CardCrypto.VerifySignature(mCard.getIssuer().getPublicDataKey(), bsDataToVerify.toByteArray(), issuerDataSignature.Value)) { - mCard.setIssuerData(issuerData.Value, issuerDataSignature.Value); - return TLVList.fromBytes(issuerData.Value); - } else { - throw new TangemException("Invalid issuer data read (signature verification failed)"); - } - } catch (Exception e) { - e.printStackTrace(); - throw new TangemException("Invalid issuer data read"); - } - } else { - throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); - } - } - - public void run_GetSupportedEncryption() throws Exception { - mCard.encryptionMode = Tangem_Card.EncryptionMode.None; - do { - CommandApdu rqApdu = StartPrepareCommand(INS.Read); - Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); - - ResponseApdu rspApdu = SendAndReceive(rqApdu, false); - - if (rspApdu.isStatus(SW.NEED_ENCRYPTION)) { - if (mCard.encryptionMode == Tangem_Card.EncryptionMode.None) { - mCard.encryptionMode = Tangem_Card.EncryptionMode.Fast; - } else if (mCard.encryptionMode == Tangem_Card.EncryptionMode.Fast) { - mCard.encryptionMode = Tangem_Card.EncryptionMode.Strong; - } else { - throw new Exception("Can't get supported encryption methods"); - } - } else if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { - Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); - readResult = rspApdu.getTLVs(); - mCard.setPIN(mPIN); - break; - } else { - break; - } - } while (true); - } - -} +package com.tangem.cardReader; + +import android.content.Context; +import android.nfc.Tag; +import android.nfc.TagLostException; +import android.nfc.tech.IsoDep; +import android.util.Log; + +import com.tangem.wallet.CoinEngine; +import com.tangem.wallet.CoinEngineFactory; +import com.tangem.wallet.Issuer; +import com.tangem.wallet.Tangem_Card; +import com.tangem.wallet.Manufacturer; + +import org.spongycastle.jce.ECNamedCurveTable; +import org.spongycastle.jce.interfaces.ECPublicKey; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.math.ec.ECPoint; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.Security; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.util.Calendar; + +import javax.crypto.KeyAgreement; + + +/** + * Created by dvol on 14.07.2017. + */ + +public class CardProtocol { + + + public interface Notifications { + void OnReadStart(CardProtocol cardProtocol); + + void OnReadProgress(CardProtocol cardProtocol, int progress); + + void OnReadFinish(CardProtocol cardProtocol); + + void OnReadCancel(); + + void OnReadWait(int msec); + + void OnReadBeforeRequest(int timeout); + + void OnReadAfterRequest(); + } + + + public void setError(Exception error) { + mError = error; + } + + public Tangem_Card getCard() { + return mCard; + } + + public Exception getError() { + return mError; + } + + private static final String logTag = "CardProtocol"; + public static final int SW_PIN_ERROR = SW.INVALID_PARAMS; + + public static final String DefaultPIN = "000000"; + public static final String DefaultPIN2 = "000"; + + protected IsoDep mIsoDep; + + public Tag getTag() { + return mIsoDep.getTag(); + } + + protected String mPIN; + protected Notifications mNotifications; + + public void setPIN(String PIN) { + mPIN = PIN; + if (mCard != null) { + mCard.setPIN(PIN); + } + } + + protected Tangem_Card mCard; + protected Exception mError; + protected Context mContext; + + static { + Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); + } + + public CardProtocol(Context context, IsoDep isoDep, Notifications notifications) { + mContext = context; + mIsoDep = isoDep; + mNotifications = notifications; + mPIN = null; + mCard = new Tangem_Card(Util.byteArrayToHexString(mIsoDep.getTag().getId())); + } + + public CardProtocol(Context context, IsoDep isoDep, Tangem_Card card, Notifications notifications) { + mContext = context; + mIsoDep = isoDep; + mNotifications = notifications; + mPIN = card.getPIN(); + mCard = card; + } + + public static class TangemException extends Exception { + public TangemException(String message) { + super(message); + } + } + + public static class TangemException_InvalidPIN extends TangemException { + public TangemException_InvalidPIN(String message) { + super(message); + } + } + + public static class TangemException_NeedPause extends TangemException { + public TangemException_NeedPause(String message) { + super(message); + } + } + + public static class TangemException_ExtendedLengthNotSupported extends TangemException { + public TangemException_ExtendedLengthNotSupported(String message) { + super(message); + } + } + + + public byte[] GetUID() { + if (mIsoDep == null || mIsoDep.getTag() == null) return null; + return mIsoDep.getTag().getId(); + } + + public int getTimeout() { + if (mIsoDep == null || mIsoDep.getTag() == null) return 60000; + return mIsoDep.getTimeout(); + } + + + protected byte[] protocolKey; + + public void resetProtocolKey() { + protocolKey = null; + } + + public void CreateProtocolKey() throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException { + protocolKey = CardCrypto.pbkdf2(Util.calculateSHA256(mPIN), mIsoDep.getTag().getId(), 50); + //Log.e("Reader", String.format("PIN: %s, Protocol key: %s", mPIN, Util.bytesToHex(protocolKey))); + if (sessionKey != null) { + sessionKey = null; + } + } + + byte[] sessionKey = null; + + public void run_OpenSession(Tangem_Card.EncryptionMode encryptionMode) throws Exception { + sessionKey = null; + try { + CommandApdu cmdApdu = new CommandApdu(CommandApdu.ISO_CLA, INS.OpenSession.Code, 0, encryptionMode.getP()); + + switch (encryptionMode) { + case Fast: { + byte[] baMyChallenge = Util.generateRandomBytes(16); + + cmdApdu.addTLV(TLV.Tag.TAG_Session_Key_A, baMyChallenge); + Log.i(logTag, cmdApdu.getCommandName()); + ResponseApdu rspApdu = null; + + try { + if (mIsoDep == null) { + throw new TagLostException(); + } + byte[] cmdBytes = cmdApdu.toBytes(); + String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc()); + Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr)); + + byte[] rsp = mIsoDep.transceive(cmdBytes); + rspApdu = new ResponseApdu(rsp); + + Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp))); + + if (rspApdu.isParsedWithError()) { + throw new Exception("Can't parse answer"); + } + } catch (Exception E) { + sessionKey = null; + throw E; + } + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + byte[] baTheirsChallenge = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Session_Key_B).Value; + if (protocolKey == null) CreateProtocolKey(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputStream.write(baMyChallenge); + outputStream.write(baTheirsChallenge); + outputStream.write(protocolKey); + sessionKey = Util.calculateSHA256(outputStream.toByteArray()); + //Log.i(logTag, String.format("Session key: %s", Util.bytesToHex(sessionKey))); + } else { + Log.e(logTag, String.format("Failed: %04X - %s", rspApdu.getSW1SW2(), rspApdu.getSW1SW2Description())); + throw new Exception(String.format("Can't open session: SW - %04X", rspApdu.getSW1SW2())); + } + } + break; + case Strong: { + KeyPairGenerator kpgen = KeyPairGenerator.getInstance("ECDH", "SC"); + kpgen.initialize(new ECGenParameterSpec("secp256k1"), new SecureRandom()); + KeyPair KP = kpgen.generateKeyPair(); + KeyAgreement ka = KeyAgreement.getInstance("ECDH", "SC"); + ka.init(KP.getPrivate()); + + ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); + //return spec.getG().multiply(new BigInteger((ECPrivateKey) )).getEncoded(false); + ECPublicKey eckey = (ECPublicKey) KP.getPublic(); + byte[] baMyPublicKey = eckey.getQ().getEncoded(false); + + cmdApdu.addTLV(TLV.Tag.TAG_Session_Key_A, baMyPublicKey); + Log.i(logTag, cmdApdu.getCommandName()); + ResponseApdu rspApdu = null; + + try { + if (mIsoDep == null) { + throw new TagLostException(); + } + //mIsoDep.setTimeout(msTimeout); + + byte[] cmdBytes = cmdApdu.toBytes(); + String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc()); + Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr)); + byte[] rsp = mIsoDep.transceive(cmdBytes); + rspApdu = new ResponseApdu(rsp); + Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp))); + + if (rspApdu.isParsedWithError()) { + throw new Exception("Can't parse answer"); + } + } catch (Exception E) { + sessionKey = null; + throw E; + } + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + byte[] baTheirsPublicKey = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Session_Key_B).Value; + ka.doPhase(CardCrypto.LoadPublicKey(baTheirsPublicKey), true); + if (protocolKey == null) CreateProtocolKey(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputStream.write(ka.generateSecret()); + outputStream.write(protocolKey); + sessionKey = Util.calculateSHA256(outputStream.toByteArray()); +// Log.i(logTag, String.format("Session key: %s", Util.bytesToHex(sessionKey))); + } else { + Log.i(logTag, String.format("Failed: %04X - %s", rspApdu.getSW1SW2(), rspApdu.getSW1SW2Description())); + throw new Exception(String.format("Can't open session: SW - %04X", rspApdu.getSW1SW2())); + } + } + break; + default: + throw new Exception("Unknown encryption mode"); + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(logTag, String.format("Exception: %s", e.getMessage())); + throw new Exception("Can't open session: " + e.getMessage()); + } + } + + + // send command APDU, get response APDU, and display HEX data to user + private ResponseApdu SendAndReceive(CommandApdu cmdApdu, boolean breakOnNeedPause) throws Exception { + if (mCard.encryptionMode != Tangem_Card.EncryptionMode.None) { + if (sessionKey == null) { + run_OpenSession(mCard.encryptionMode); + } + cmdApdu.Crypt(sessionKey); + } + cmdApdu.setP1(mCard.encryptionMode.getP()); + byte[] cmdBytes = cmdApdu.toBytes(); + String cmdStr = CommandApdu.toString(cmdBytes, cmdApdu.getLc()); + Log.v("NFC", String.format("<< [%s]: %s", cmdApdu.getCommandName(), cmdStr)); + byte[] rsp; + ResponseApdu rspApdu; + try { + do { + try { + mNotifications.OnReadBeforeRequest(mIsoDep.getTimeout()); + try { + rsp = mIsoDep.transceive(cmdBytes); + } finally { + mNotifications.OnReadAfterRequest(); + } + } catch (IOException e) { + if (e.getMessage().contains("length exceeds supported maximum") && mIsoDep.isExtendedLengthApduSupported()) { + throw new TangemException_ExtendedLengthNotSupported(e.getMessage()); + } + throw e; + } + if (mCard.encryptionMode != Tangem_Card.EncryptionMode.None) { + rspApdu = ResponseApdu.Decrypt(rsp, sessionKey); + } else { + rspApdu = new ResponseApdu(rsp); + } + + if (rspApdu.isParsedWithError()) { + Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp))); + throw new TangemException(rspApdu.getParseErroMessage()); + } else if (rspApdu.getSW1SW2() == SW.NEED_PAUSE && mNotifications != null) { + int remainingPause = rspApdu.getTLVs().getTagAsInt(TLV.Tag.TAG_Pause) * 10; + Log.v("NFC", String.format(">> Security delay, remaining %f s", remainingPause / 1000.0)); + if (breakOnNeedPause) { + break; + } else { + mNotifications.OnReadWait(remainingPause); + } + } else { + Log.v("NFC", String.format(">> [%s]: %s", cmdApdu.getCommandName(), Util.bytesToHex(rsp))); + } + } while (rspApdu.getSW1SW2() == SW.NEED_PAUSE); + } finally { + mNotifications.OnReadWait(0); + } + + return rspApdu; + } + + private CommandApdu StartPrepareCommand(INS ins) throws NoSuchAlgorithmException { + CommandApdu Apdu = new CommandApdu(ins); + byte[] baPIN = Util.calculateSHA256(mPIN); + Apdu.addTLV(TLV.Tag.TAG_PIN, baPIN); + if (ins != INS.Read) { + Apdu.addTLV(TLV.Tag.TAG_CardID, mCard.getCID()); + } + return Apdu; + } + + public void run_Read() throws Exception { + CommandApdu rqApdu = StartPrepareCommand(INS.Read); + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + + + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + + + readResult = rspApdu.getTLVs(); + + parseReadResult(); + + run_ReadOrWriteIssuerDataAndDefineOfflineBalance(); + + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible PIN is invalid!\n", rspApdu.getSW1SW2())); + } else { + throw new TangemException(String.format("FAILED: [%04X]\n", rspApdu.getSW1SW2())); + } + } + + public void run_ReadOrWriteIssuerDataAndDefineOfflineBalance() throws Exception { + TLVList tlvIssuerData; + if (mCard.getNeedWriteIssuerData()) { + run_WriteIssuerData(mCard.getIssuerData(), mCard.getIssuerDataSignature()); + try { + tlvIssuerData = TLVList.fromBytes(mCard.getIssuerData()); + } catch (TLVException e) { + e.printStackTrace(); + tlvIssuerData = null; + } + } else { + try { + tlvIssuerData = run_ReadIssuerData(); + } catch (Exception e) { + e.printStackTrace(); + tlvIssuerData = null; + } + } + if (mCard.getStatus() == Tangem_Card.Status.Loaded) { + // try read offline balance data + if (tlvIssuerData != null && tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination) != null && mCard.getMaxSignatures() == mCard.getRemainingSignatures()) { + mCard.setOfflineBalance(tlvIssuerData.getTLV(TLV.Tag.TAG_Denomination).Value); + } else { + mCard.clearOfflineBalance(); + } + } else { + mCard.clearOfflineBalance(); + } + } + + private TLVList readResult = null; + + public void clearReadResult() { + sessionKey = null; + readResult = null; + } + + public boolean haveReadResult() { + return readResult != null; + } + + public void parseReadResult() throws TangemException, NoSuchProviderException, NoSuchAlgorithmException { + TLV tlvStatus = readResult.getTLV(TLV.Tag.TAG_Status); + mCard.setStatus(Tangem_Card.Status.fromCode(Util.byteArrayToInt(tlvStatus.Value))); + TLV tlvCID = readResult.getTLV(TLV.Tag.TAG_CardID); + mCard.setCID(tlvCID.Value); + mCard.setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString()), true); + mCard.setHealth(readResult.getTLV(TLV.Tag.TAG_Health).getAsInt()); + + if (mCard.getStatus() != Tangem_Card.Status.NotPersonalized) { + try { + TLV tlvCardPubkicKey = readResult.getTLV(TLV.Tag.TAG_CardPublicKey); + if (tlvCardPubkicKey == null) + throw new TangemException("Invalid answer format"); + mCard.setCardPublicKey(tlvCardPubkicKey.Value); + + TLVList tlvCardData = TLVList.fromBytes(readResult.getTLV(TLV.Tag.TAG_CardData).Value); + + TLV tokenSymbol = tlvCardData.getTLV(TLV.Tag.TAG_Token_Symbol); + TLV contractAddress = tlvCardData.getTLV(TLV.Tag.TAG_Token_Contract_Address); + TLV tokens_decimal = tlvCardData.getTLV(TLV.Tag.TAG_Token_Decimal); + + if (tokenSymbol != null) + mCard.setTokenSymbol(tokenSymbol.getAsString()); + + if (contractAddress != null) + mCard.setContractAddress(contractAddress.getAsString()); + + if (tokens_decimal != null) + mCard.setTokensDecimal(tokens_decimal.getAsInt()); + + // this method has reflection TokenSymbol + // you mast call setBlockchainIDFromCard after calling setTokensDecimal + mCard.setBlockchainIDFromCard(tlvCardData.getTLV(TLV.Tag.TAG_Blockchain_ID).getAsString()); + byte[] tlvPersonalizationDT = tlvCardData.getTLV(TLV.Tag.TAG_ManufactureDateTime).Value; + int year = (tlvPersonalizationDT[0] & 0xFF) << 8 | (tlvPersonalizationDT[1] & 0xFF); + int month = tlvPersonalizationDT[2] - 1; + int day = tlvPersonalizationDT[3]; + Calendar cd = Calendar.getInstance(); + cd.set(year, month, day, 0, 0, 0); + mCard.setPersonalizationDateTime(cd.getTime()); + try { + if (readResult.getTLV(TLV.Tag.TAG_Firmware) != null) { + mCard.setFirmwareVersion(readResult.getTLV(TLV.Tag.TAG_Firmware).getAsString()); + } else { + mCard.setFirmwareVersion(tlvCardData.getTLV(TLV.Tag.TAG_Firmware).getAsString()); + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(logTag, "Cannot get firmware version"); + mCard.setFirmwareVersion("0.00"); + } + + try { + if (mCard.getFirmwareVersion().compareTo("1.05") < 0) { + mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value)); + } else { + mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Data_PublicKey).Value)); + } + } catch (Exception e) { + e.printStackTrace(); + Log.e(logTag, "Cannot get issuer, try a version for older cards"); + try { + mCard.setIssuer(Issuer.FindIssuer(tlvCardData.getTLV(TLV.Tag.TAG_Issuer_ID).getAsString(), readResult.getTLV(TLV.Tag.TAG_Issuer_Transaction_PublicKey).Value)); + } catch (Exception ee) { + ee.printStackTrace(); + Log.e(logTag, "Cannot get issuer"); + mCard.setIssuer(Issuer.Unknown); + } + } + + try { + mCard.setSettingsMask(readResult.getTagAsInt(TLV.Tag.TAG_SettingsMask)); + } catch (Exception e) { + e.printStackTrace(); + Log.e(logTag, "Can't get settings mask"); + } + + if (readResult.getTLV(TLV.Tag.TAG_PauseBeforePIN2) != null) { + mCard.setPauseBeforePIN2(10 * readResult.getTagAsInt(TLV.Tag.TAG_PauseBeforePIN2)); + } + + try { + mCard.setSigningMethod(readResult.getTagAsInt(TLV.Tag.TAG_SigningMethod)); + } catch (Exception e) { + e.printStackTrace(); + Log.e(logTag, "Can't get signing method"); + mCard.setSigningMethod(0); + } + + try { + mCard.setMaxSignatures(readResult.getTagAsInt(TLV.Tag.TAG_MaxSignatures)); + } catch (Exception e) { + e.printStackTrace(); + Log.e(logTag, "Can't get max signatures"); + } + + } catch (Exception e) { + e.printStackTrace(); + throw new TangemException("Can't parse card data"); + } + } + + + if (mCard.getStatus() == Tangem_Card.Status.Loaded) { + + TLV tlvPublicKey = readResult.getTLV(TLV.Tag.TAG_Wallet_PublicKey); + + ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); + ECPoint p1 = spec.getCurve().decodePoint(tlvPublicKey.Value); + + byte pkUncompressed[] = p1.getEncoded(false); + + byte pkCompresses[] = p1.getEncoded(true); + mCard.setWalletPublicKey(pkUncompressed); + mCard.setWalletPublicKeyRar(pkCompresses); + + CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain()); + String wallet = engineCoin.calculateAddress(mCard, pkUncompressed); + mCard.setWallet(wallet); + //mCard.setWallet(Blockchain.calculateWalletAddress(mCard, pkUncompressed)); + + mCard.setRemainingSignatures(readResult.getTagAsInt(TLV.Tag.TAG_RemainingSignatures)); + + } else { + mCard.setWallet("N/A"); + } + } + + + public TLVList run_VerifyCard() throws Exception { + if (mCard.getCardPublicKey() == null || readResult == null) { + run_Read(); + } + if (mCard.getStatus() == Tangem_Card.Status.NotPersonalized) { + getCard().setManufacturer(Manufacturer.Unknown, false); + return null; + } + + if (mCard.getCardPublicKey() == null) { + throw new TangemException("Not all data read, can't verify card!"); + } + + CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCard); + byte[] bChallenge = Util.generateRandomBytes(16); + rqApdu.addTLV(TLV.Tag.TAG_Challenge, bChallenge); + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + TLVList verifyResult = rspApdu.getTLVs(); + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + verifyResult.add(new TLV(TLV.Tag.TAG_Challenge, bChallenge)); + + TLV tlvSalt = verifyResult.getTLV(TLV.Tag.TAG_Salt); + TLV tlvCardSignature = verifyResult.getTLV(TLV.Tag.TAG_CardSignature); +// TLV tlvManufacturerSignature = verifyResult.getTLV(TLV.Tag.TAG_Manufacturer_Signature); + + if (tlvSalt == null || tlvCardSignature == null) { + throw new TangemException("Not all data read, can't verify card!"); + } + + try { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + bs.write(bChallenge); + bs.write(tlvSalt.Value); + byte[] dataArray = bs.toByteArray(); + if (CardCrypto.VerifySignature(mCard.getCardPublicKey(), dataArray, tlvCardSignature.Value)) { + getCard().setCardPublicKeyValid(true); + Log.i(logTag, "Card signature verification OK"); + } else { + Log.e(logTag, "Card signature verification FAILED"); + getCard().setCardPublicKeyValid(false); + } + + //getCard().setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString(), bChallenge, tlvSalt.Value, tlvManufacturerSignature.Value), true); + getCard().setManufacturer(Manufacturer.FindManufacturer(readResult.getTLV(TLV.Tag.TAG_Manufacture_ID).getAsString()), true); + } catch (Exception e) { + e.printStackTrace(); + getCard().setManufacturer(Manufacturer.Unknown, false); + } + return verifyResult; + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); + } else { + getCard().setManufacturer(Manufacturer.Unknown, false); + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + + } + + public void run_CreateWallet(String PIN2) throws Exception { + if (readResult == null) run_Read(); + 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(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(true); + } + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(false); + } + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + public TLVList run_CheckWallet() throws Exception { + CommandApdu rqApdu = StartPrepareCommand(INS.CheckWallet); + byte[] bChallenge = Util.generateRandomBytes(16); + rqApdu.addTLV(TLV.Tag.TAG_Challenge, bChallenge); + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + TLVList Result = rspApdu.getTLVs(); + Result.add(new TLV(TLV.Tag.TAG_Challenge, bChallenge)); + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + return Result; + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + public void run_CheckWalletWithSignatureVerify() throws Exception { + if (mCard.getCardPublicKey() == null || readResult == null) { + run_Read(); + } + if (mCard.getStatus() == Tangem_Card.Status.NotPersonalized) { + getCard().setManufacturer(Manufacturer.Unknown, false); + return; + } + + if (readResult.getTagAsInt(TLV.Tag.TAG_Status) != Tangem_Card.Status.Loaded.getCode()) { + throw new TangemException("Card must be loaded"); + } + TLVList checkResult = run_CheckWallet(); + if (checkResult == null) return; + + TLV tlvPublicKey = readResult.getTLV(TLV.Tag.TAG_Wallet_PublicKey); + TLV tlvChallenge = checkResult.getTLV(TLV.Tag.TAG_Challenge); + TLV tlvSalt = checkResult.getTLV(TLV.Tag.TAG_Salt); + TLV tlvSignature = checkResult.getTLV(TLV.Tag.TAG_Signature); + + if (tlvPublicKey == null || tlvChallenge == null || tlvSalt == null || tlvSignature == null) { + throw new TangemException("Not all data read, can't check signature!"); + } + + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + bs.write(tlvChallenge.Value); + bs.write(tlvSalt.Value); + byte[] dataArray = bs.toByteArray(); + + if (CardCrypto.VerifySignature(tlvPublicKey.Value, dataArray, tlvSignature.Value)) { + Log.i(logTag, "Signature verification OK"); + mCard.setWalletPublicKeyValid(true); + } else { + mCard.setWalletPublicKeyValid(false); + } + } + + public void run_PurgeWallet(String PIN2) throws Exception { + CommandApdu rqApdu = StartPrepareCommand(INS.PurgeWallet); + rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2)); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(true); + } + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(false); + } + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); + } else { + + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + public void run_SwapPIN(String PIN2, String newPin, String newPin2, boolean breakOnNeedPause) throws Exception { + CommandApdu rqApdu = StartPrepareCommand(INS.SwapPIN); + rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2)); + rqApdu.addTLV(TLV.Tag.TAG_NewPIN, Util.calculateSHA256(newPin)); + rqApdu.addTLV(TLV.Tag.TAG_NewPIN2, Util.calculateSHA256(newPin2)); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, breakOnNeedPause); + + if (rspApdu.isStatus(SW.PIN1_CHANGED) || rspApdu.isStatus(SW.PIN2_CHANGED) || rspApdu.isStatus(SW.PINS_CHANGED) || rspApdu.isStatus(SW.PINS_NOT_CHANGED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + if (newPin2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(true); + } else { + mCard.setUseDefaultPIN2(false); + } + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(false); + } + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Invalid PIN\n", rspApdu.getSW1SW2())); + } else if (breakOnNeedPause && rspApdu.isStatus(SW.NEED_PAUSE)) { + throw new TangemException_NeedPause(String.format("FAILED: [%04X] - Need pause\n", rspApdu.getSW1SW2())); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + public void run_CheckPIN2isDefault() throws Exception { + if (mCard.isFirmwareNewer("1.19") || (mCard.isFirmwareNewer("1.12") && (mCard.getPauseBeforePIN2() == 0 || mCard.useSmartSecurityDelay()))) { + // can obtain SwapPIN(to default) answer without security delay - try check if PIN2 is default with card request + try { + run_SwapPIN(DefaultPIN2, mPIN, DefaultPIN2, true); + mCard.setUseDefaultPIN2(true); + } catch (TangemException_NeedPause e) { + mCard.setUseDefaultPIN2(null); + } catch (TangemException_InvalidPIN e) { + mCard.setUseDefaultPIN2(false); + } + } else { + mCard.setUseDefaultPIN2(null); + } + } + + public TLVList run_SignHashes(String PIN2, byte[][] hashes, boolean UseIssuerValidation, byte[] issuerData, Issuer issuer) throws Exception { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + if (hashes.length > 10) throw new Exception("To much hashes in one transaction!"); + for (int i = 0; i < hashes.length; i++) { + if (i != 0 && hashes[0].length != hashes[i].length) + throw new Exception("Hashes length must be identical!"); + bs.write(hashes[i]); + } + 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()); + if (UseIssuerValidation) { + byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), bs.toByteArray()); + rqApdu.addTLV(TLV.Tag.TAG_Issuer_Transaction_Signature, issuerSignature); + } + if (issuerData != null) { + if (issuer == null || issuer == Issuer.Unknown) + throw new Exception("Need known Issuer to write issuer Data"); + rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData); + byte[] issuerSignature = CardCrypto.Signature(issuer.getPrivateTransactionKey(), issuerData); + rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerSignature); + } + + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + TLVList Result = rspApdu.getTLVs(); + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(true); + } + return Result; + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(false); + } + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2())); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + public TLVList run_SignRaw(String PIN2, byte[] bTxOutData) throws Exception { + + CommandApdu rqApdu = StartPrepareCommand(INS.Sign); + rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2)); + rqApdu.addTLV(TLV.Tag.TAG_TrOut_Raw, bTxOutData); + rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, "sha-256x2".getBytes("US-ASCII")); + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + TLVList Result = rspApdu.getTLVs(); + Result.add(new TLV(TLV.Tag.TAG_TrOut_Raw, bTxOutData)); + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(true); + } + return Result; + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(false); + } + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2())); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + private byte[] run_VerifyCode(String hashAlgID, int codePageAddress, int codePageCount, byte[] challenge) throws Exception { + if (readResult == null) run_Read(); + CommandApdu rqApdu = StartPrepareCommand(INS.VerifyCode); + rqApdu.addTLV(TLV.Tag.TAG_HashAlgID, hashAlgID.getBytes("US-ASCII")); + rqApdu.addTLV_U32(TLV.Tag.TAG_CodePageAddress, codePageAddress); + rqApdu.addTLV_U16(TLV.Tag.TAG_CodePageCount, codePageCount); + rqApdu.addTLV(TLV.Tag.TAG_Challenge, challenge); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + return rspApdu.getTLVs().getTLV(TLV.Tag.TAG_CodeHash).Value; + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + private void run_ValidateCard(String PIN2) throws Exception { + if (readResult == null) run_Read(); + CommandApdu rqApdu = StartPrepareCommand(INS.ValidateCard); + rqApdu.addTLV(TLV.Tag.TAG_PIN2, Util.calculateSHA256(PIN2)); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(true); + } + } else if (rspApdu.isStatus(SW_PIN_ERROR)) { + if (PIN2.equals(DefaultPIN2)) { + mCard.setUseDefaultPIN2(false); + } + throw new TangemException_InvalidPIN(String.format("FAILED: [%04X] - Possible the PIN or PIN2 is invalid!\n", rspApdu.getSW1SW2())); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + private void run_WriteIssuerData(byte[] issuerData, byte[] issuerSignature) throws Exception { + run_Read(); + + CommandApdu rqApdu = StartPrepareCommand(INS.WriteIssuerData); + rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data, issuerData); + rqApdu.addTLV(TLV.Tag.TAG_Issuer_Data_Signature, issuerSignature); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + private TLVList run_ReadIssuerData() throws Exception { + CommandApdu rqApdu = StartPrepareCommand(INS.GetIssuerData); + + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + TLV issuerData = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data); + TLV issuerDataSignature = rspApdu.getTLVs().getTLV(TLV.Tag.TAG_Issuer_Data_Signature); + + if (issuerData == null || issuerDataSignature == null) + throw new TangemException("Invalid answer format (GetIssuerData)"); + + ByteArrayOutputStream bsDataToVerify = new ByteArrayOutputStream(); + bsDataToVerify.write(mCard.getCID()); + bsDataToVerify.write(issuerData.Value); + try { + if (CardCrypto.VerifySignature(mCard.getIssuer().getPublicDataKey(), bsDataToVerify.toByteArray(), issuerDataSignature.Value)) { + mCard.setIssuerData(issuerData.Value, issuerDataSignature.Value); + return TLVList.fromBytes(issuerData.Value); + } else { + throw new TangemException("Invalid issuer data read (signature verification failed)"); + } + } catch (Exception e) { + e.printStackTrace(); + throw new TangemException("Invalid issuer data read"); + } + } else { + throw new TangemException(String.format("Failed: %04X", rspApdu.getSW1SW2())); + } + } + + public void run_GetSupportedEncryption() throws Exception { + mCard.encryptionMode = Tangem_Card.EncryptionMode.None; + do { + CommandApdu rqApdu = StartPrepareCommand(INS.Read); + Log.i(logTag, String.format("[%s]\n%s", rqApdu.getCommandName(), rqApdu.getTLVs().getParsedTLVs(" "))); + + ResponseApdu rspApdu = SendAndReceive(rqApdu, false); + + if (rspApdu.isStatus(SW.NEED_ENCRYPTION)) { + if (mCard.encryptionMode == Tangem_Card.EncryptionMode.None) { + mCard.encryptionMode = Tangem_Card.EncryptionMode.Fast; + } else if (mCard.encryptionMode == Tangem_Card.EncryptionMode.Fast) { + mCard.encryptionMode = Tangem_Card.EncryptionMode.Strong; + } else { + throw new Exception("Can't get supported encryption methods"); + } + } else if (rspApdu.isStatus(SW.PROCESS_COMPLETED)) { + Log.i(logTag, String.format("OK: [%04X]\n%s", rspApdu.getSW1SW2(), rspApdu.getTLVs().getParsedTLVs(" "))); + readResult = rspApdu.getTLVs(); + mCard.setPIN(mPIN); + break; + } else { + break; + } + } while (true); + } + +} diff --git a/app/src/main/java/com/tangem/cardReader/INS.java b/app/src/main/java/com/tangem/cardReader/INS.java index 8212ff802c..c9b26ca5c0 100644 --- a/app/src/main/java/com/tangem/cardReader/INS.java +++ b/app/src/main/java/com/tangem/cardReader/INS.java @@ -1,43 +1,43 @@ -package com.tangem.cardReader; - -/** - * Created by dvol on 07.03.2018. - */ -public enum INS { - Unknown(0x00), - BootROM_SOS(0x40), - BootROM_Tangem(0xF0), - Personalize(0xF1), - Read(0xF2), - VerifyCard(0xF3), - ValidateCard(0xF4), - VerifyCode(0xF5), - WriteIssuerData(0xF6), - GetIssuerData(0xF7), - CreateWallet(0xF8), - CheckWallet(0xF9), - SwapPIN(0xFA), - Sign(0xFB), - PurgeWallet(0xFC), - Activate(0xFE), - OpenSession(0xFF), - ReadBlockedData(0xE4), - CreateTestWallet(0xE0), - ExtractWalletKey(0xE1), - Test(0xE2), - Depersonalize(0xE3); - - INS(int Code) { - this.Code = Code; - } - - public int Code; - - public static INS ByCode(int Code) { - INS[] allINS = INS.values(); - for (INS i : allINS) { - if (i.Code == Code) return i; - } - return Unknown; - } -} +package com.tangem.cardReader; + +/** + * Created by dvol on 07.03.2018. + */ +public enum INS { + Unknown(0x00), + BootROM_SOS(0x40), + BootROM_Tangem(0xF0), + Personalize(0xF1), + Read(0xF2), + VerifyCard(0xF3), + ValidateCard(0xF4), + VerifyCode(0xF5), + WriteIssuerData(0xF6), + GetIssuerData(0xF7), + CreateWallet(0xF8), + CheckWallet(0xF9), + SwapPIN(0xFA), + Sign(0xFB), + PurgeWallet(0xFC), + Activate(0xFE), + OpenSession(0xFF), + ReadBlockedData(0xE4), + CreateTestWallet(0xE0), + ExtractWalletKey(0xE1), + Test(0xE2), + Depersonalize(0xE3); + + INS(int Code) { + this.Code = Code; + } + + public int Code; + + public static INS ByCode(int Code) { + INS[] allINS = INS.values(); + for (INS i : allINS) { + if (i.Code == Code) return i; + } + return Unknown; + } +} diff --git a/app/src/main/java/com/tangem/cardReader/NFCEnableDialog.java b/app/src/main/java/com/tangem/cardReader/NFCEnableDialog.java index 5eb2fd2413..d970fd1b9e 100644 --- a/app/src/main/java/com/tangem/cardReader/NFCEnableDialog.java +++ b/app/src/main/java/com/tangem/cardReader/NFCEnableDialog.java @@ -1,45 +1,45 @@ -package com.tangem.cardReader; - -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.provider.Settings; -import android.support.v7.app.AlertDialog; - -import com.tangem.wallet.R; - -/** - * Created by dvol on 18.02.2018. - */ - -public class NFCEnableDialog extends DialogFragment { - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setCancelable(false) - .setIcon(R.drawable.ic_action_nfc_gray) - .setTitle(R.string.nfc_disabled) - .setMessage(R.string.enable_nfc) - .setPositiveButton(R.string.dialog_ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int id) { - // take user to wireless settings - getActivity().startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS)); - } - }) - .setNegativeButton(R.string.dialog_quit, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int id) { - dialog.cancel(); - getActivity().finish(); - } - }); - return builder.create(); - } -} +package com.tangem.cardReader; + +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.provider.Settings; +import android.support.v7.app.AlertDialog; + +import com.tangem.wallet.R; + +/** + * Created by dvol on 18.02.2018. + */ + +public class NFCEnableDialog extends DialogFragment { + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setCancelable(false) + .setIcon(R.drawable.ic_action_nfc_gray) + .setTitle(R.string.nfc_disabled) + .setMessage(R.string.enable_nfc) + .setPositiveButton(R.string.dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int id) { + // take user to wireless settings + getActivity().startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS)); + } + }) + .setNegativeButton(R.string.dialog_quit, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int id) { + dialog.cancel(); + getActivity().finish(); + } + }); + return builder.create(); + } +} diff --git a/app/src/main/java/com/tangem/cardReader/NfcManager.java b/app/src/main/java/com/tangem/cardReader/NfcManager.java index 7debf041aa..17f819a329 100644 --- a/app/src/main/java/com/tangem/cardReader/NfcManager.java +++ b/app/src/main/java/com/tangem/cardReader/NfcManager.java @@ -1,153 +1,153 @@ -package com.tangem.cardReader; - -import android.Manifest; -import android.annotation.TargetApi; -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.IsoDep; -import android.os.Build; -import android.os.Bundle; -import android.support.v4.app.ActivityCompat; -import android.util.Log; - -import java.io.IOException; - -public class NfcManager { - - private static final String TAG = "NfcManager"; - - - // reader mode flags: listen for type A (not B), skipping ndef check - private static final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS; - - private NfcAdapter mNfcAdapter; - private NFCEnableDialog mEnableNfcDialog; - private Activity mActivity; - private NfcAdapter.ReaderCallback mReaderCallback; - - private boolean broadcomWorkaround = false; - private static final int DELAY_PRESENCE = 1500; - - public NfcManager(Activity activity, NfcAdapter.ReaderCallback readerCallback) { - mActivity = activity; - mReaderCallback = readerCallback; - mNfcAdapter = NfcAdapter.getDefaultAdapter(activity); - } - - public void onResume() { - // register broadcast receiver - IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); - mActivity.registerReceiver(mBroadcastReceiver, filter); - - if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { - ShowNFCEnableDialog(); - - } else { - enableReaderMode(); - } - } - - public void ShowNFCEnableDialog() { - mEnableNfcDialog=new NFCEnableDialog(); - mEnableNfcDialog.show(mActivity.getFragmentManager(),"NFCEnableDialog"); - } - - public void onPause() { - mActivity.unregisterReceiver(mBroadcastReceiver); - disableReaderMode(); - } - - public void onStop() { - if (mEnableNfcDialog != null) { - mEnableNfcDialog.dismiss(); - } - } - - public void IgnoreTag(Tag tag) throws IOException { -// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { -// mNfcAdapter.ignore(tag, 500, null, null); -// }else{ - IsoDep isoDep = IsoDep.get(tag); - if (isoDep != null) { - isoDep.close(); - } -// } - } - - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @SuppressWarnings("deprecation") - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action == null) - return; - if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) { - int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, - NfcAdapter.STATE_ON); - if (state == NfcAdapter.STATE_ON - || state == NfcAdapter.STATE_TURNING_ON) { - Log.d(TAG, "state: " + state + " , dialog: " - + mEnableNfcDialog); - if (mEnableNfcDialog != null) { - mEnableNfcDialog.dismiss(); - } - if (state == NfcAdapter.STATE_ON) { - enableReaderMode(); - } - } else { - if (mEnableNfcDialog == null || !mEnableNfcDialog.isVisible()) { - ShowNFCEnableDialog(); - } - } - } - } - }; - - @TargetApi(Build.VERSION_CODES.KITKAT) - private void enableReaderMode() { - Bundle options = new Bundle(); - if (broadcomWorkaround) { - /* This is a work around for some Broadcom chipsets that does - * the presence check by sending commands that interrupt the - * processing of the ongoing command. - */ - options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, DELAY_PRESENCE); - } - mNfcAdapter.enableReaderMode(mActivity, mReaderCallback, READER_FLAGS, options); - } - - @TargetApi(Build.VERSION_CODES.KITKAT) - private void disableReaderMode() { - if (mNfcAdapter != null) { - mNfcAdapter.disableReaderMode(mActivity); - } - } - - private static final int REQUEST_NFC_PERMISSIONS = 1; - private static String[] PERMISSIONS_NFC = { - Manifest.permission.NFC - }; - - //Checks if the app has NFC permission - //If the app does not has permission then the user will be prompted to grant permissions - public static void verifyPermissions(Activity activity) { - // Check if we have write permission - int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.NFC); - - if (permission != PackageManager.PERMISSION_GRANTED) { - // We don't have permission so prompt the user - ActivityCompat.requestPermissions( - activity, - PERMISSIONS_NFC, - REQUEST_NFC_PERMISSIONS - ); - } - } -} - +package com.tangem.cardReader; + +import android.Manifest; +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.util.Log; + +import java.io.IOException; + +public class NfcManager { + + private static final String TAG = "NfcManager"; + + + // reader mode flags: listen for type A (not B), skipping ndef check + private static final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS; + + private NfcAdapter mNfcAdapter; + private NFCEnableDialog mEnableNfcDialog; + private Activity mActivity; + private NfcAdapter.ReaderCallback mReaderCallback; + + private boolean broadcomWorkaround = false; + private static final int DELAY_PRESENCE = 1500; + + public NfcManager(Activity activity, NfcAdapter.ReaderCallback readerCallback) { + mActivity = activity; + mReaderCallback = readerCallback; + mNfcAdapter = NfcAdapter.getDefaultAdapter(activity); + } + + public void onResume() { + // register broadcast receiver + IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); + mActivity.registerReceiver(mBroadcastReceiver, filter); + + if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { + ShowNFCEnableDialog(); + + } else { + enableReaderMode(); + } + } + + public void ShowNFCEnableDialog() { + mEnableNfcDialog=new NFCEnableDialog(); + mEnableNfcDialog.show(mActivity.getFragmentManager(),"NFCEnableDialog"); + } + + public void onPause() { + mActivity.unregisterReceiver(mBroadcastReceiver); + disableReaderMode(); + } + + public void onStop() { + if (mEnableNfcDialog != null) { + mEnableNfcDialog.dismiss(); + } + } + + public void IgnoreTag(Tag tag) throws IOException { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { +// mNfcAdapter.ignore(tag, 500, null, null); +// }else{ + IsoDep isoDep = IsoDep.get(tag); + if (isoDep != null) { + isoDep.close(); + } +// } + } + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @SuppressWarnings("deprecation") + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action == null) + return; + if (action.equals(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)) { + int state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, + NfcAdapter.STATE_ON); + if (state == NfcAdapter.STATE_ON + || state == NfcAdapter.STATE_TURNING_ON) { + Log.d(TAG, "state: " + state + " , dialog: " + + mEnableNfcDialog); + if (mEnableNfcDialog != null) { + mEnableNfcDialog.dismiss(); + } + if (state == NfcAdapter.STATE_ON) { + enableReaderMode(); + } + } else { + if (mEnableNfcDialog == null || !mEnableNfcDialog.isVisible()) { + ShowNFCEnableDialog(); + } + } + } + } + }; + + @TargetApi(Build.VERSION_CODES.KITKAT) + private void enableReaderMode() { + Bundle options = new Bundle(); + if (broadcomWorkaround) { + /* This is a work around for some Broadcom chipsets that does + * the presence check by sending commands that interrupt the + * processing of the ongoing command. + */ + options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, DELAY_PRESENCE); + } + mNfcAdapter.enableReaderMode(mActivity, mReaderCallback, READER_FLAGS, options); + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + private void disableReaderMode() { + if (mNfcAdapter != null) { + mNfcAdapter.disableReaderMode(mActivity); + } + } + + private static final int REQUEST_NFC_PERMISSIONS = 1; + private static String[] PERMISSIONS_NFC = { + Manifest.permission.NFC + }; + + //Checks if the app has NFC permission + //If the app does not has permission then the user will be prompted to grant permissions + public static void verifyPermissions(Activity activity) { + // Check if we have write permission + int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.NFC); + + if (permission != PackageManager.PERMISSION_GRANTED) { + // We don't have permission so prompt the user + ActivityCompat.requestPermissions( + activity, + PERMISSIONS_NFC, + REQUEST_NFC_PERMISSIONS + ); + } + } +} + diff --git a/app/src/main/java/com/tangem/cardReader/SW.java b/app/src/main/java/com/tangem/cardReader/SW.java index 395d6361b9..3765acbb74 100644 --- a/app/src/main/java/com/tangem/cardReader/SW.java +++ b/app/src/main/java/com/tangem/cardReader/SW.java @@ -1,43 +1,43 @@ -package com.tangem.cardReader; - -/** - * Created by dvol on 07.03.2018. - */ - -public class SW { - public static final int PROCESS_COMPLETED = 0x9000; - public static final int INVALID_PARAMS = 0x6A86; - public static final int ERROR_PROCESSING_COMMAND = 0x6286; - public static final int INVALID_STATE = 0x6985; - public static final int PINS_NOT_CHANGED = PROCESS_COMPLETED; - public static final int PIN1_CHANGED = PROCESS_COMPLETED + 0x0001; - public static final int PIN2_CHANGED = PROCESS_COMPLETED + 0x0002; - public static final int PINS_CHANGED = PROCESS_COMPLETED + 0x0003; - public static final int INS_NOT_SUPPORTED = 0x6D00; - public static final int NEED_ENCRYPTION = 0x6982; - public static final int NEED_PAUSE = 0x9789; - - public static String getDescription(int sw) { - switch (sw) { - case ERROR_PROCESSING_COMMAND: - return "SW_ERROR_PROCESSING_COMMAND"; - case INVALID_PARAMS: - return "SW_INVALID_PARAMS"; - case INVALID_STATE: - return "SW_INVALID_STATE"; - case INS_NOT_SUPPORTED: - return "SW_INS_NOT_SUPPORTED"; - case NEED_ENCRYPTION: - return "SW_NEED_ENCRYPTION"; - case PIN1_CHANGED: - return "SW_PIN1_CHANGED"; - case PIN2_CHANGED: - return "SW_PIN2_CHANGED"; - case PINS_CHANGED: - return "SW_PINS_CHANGED"; - case PROCESS_COMPLETED: - return "SW_PROCESS_COMPLETED"; - } - return "???"; - } -} +package com.tangem.cardReader; + +/** + * Created by dvol on 07.03.2018. + */ + +public class SW { + public static final int PROCESS_COMPLETED = 0x9000; + public static final int INVALID_PARAMS = 0x6A86; + public static final int ERROR_PROCESSING_COMMAND = 0x6286; + public static final int INVALID_STATE = 0x6985; + public static final int PINS_NOT_CHANGED = PROCESS_COMPLETED; + public static final int PIN1_CHANGED = PROCESS_COMPLETED + 0x0001; + public static final int PIN2_CHANGED = PROCESS_COMPLETED + 0x0002; + public static final int PINS_CHANGED = PROCESS_COMPLETED + 0x0003; + public static final int INS_NOT_SUPPORTED = 0x6D00; + public static final int NEED_ENCRYPTION = 0x6982; + public static final int NEED_PAUSE = 0x9789; + + public static String getDescription(int sw) { + switch (sw) { + case ERROR_PROCESSING_COMMAND: + return "SW_ERROR_PROCESSING_COMMAND"; + case INVALID_PARAMS: + return "SW_INVALID_PARAMS"; + case INVALID_STATE: + return "SW_INVALID_STATE"; + case INS_NOT_SUPPORTED: + return "SW_INS_NOT_SUPPORTED"; + case NEED_ENCRYPTION: + return "SW_NEED_ENCRYPTION"; + case PIN1_CHANGED: + return "SW_PIN1_CHANGED"; + case PIN2_CHANGED: + return "SW_PIN2_CHANGED"; + case PINS_CHANGED: + return "SW_PINS_CHANGED"; + case PROCESS_COMPLETED: + return "SW_PROCESS_COMPLETED"; + } + return "???"; + } +} diff --git a/app/src/main/java/com/tangem/cardReader/SettingsMask.java b/app/src/main/java/com/tangem/cardReader/SettingsMask.java index 973ccc5544..b35815ab0e 100644 --- a/app/src/main/java/com/tangem/cardReader/SettingsMask.java +++ b/app/src/main/java/com/tangem/cardReader/SettingsMask.java @@ -1,25 +1,25 @@ -package com.tangem.cardReader; - -/** - * Created by dvol on 07.03.2018. - */ - -public class SettingsMask { - public static final int IsReusable = 0x0001; - public static final int UseActivation = 0x0002; - public static final int UseBlock = 0x0008; - - public static final int AllowSwapPIN = 0x0010; - public static final int AllowSwapPIN2 = 0x0020; - public static final int UseCVC = 0x0040; - public static final int ForbidDefaultPIN = 0x0080; - - public static final int UseOneCommandAtTime = 0x0100; - public static final int UseNDEF = 0x0200; - public static final int UseDynamicNDEF = 0x0400; - public static final int SmartSecurityDelay = 0x0800; - - public static final int Protocol_AllowUnencrypted = 0x1000; - public static final int Protocol_AllowStaticEncryption = 0x2000; - -} +package com.tangem.cardReader; + +/** + * Created by dvol on 07.03.2018. + */ + +public class SettingsMask { + public static final int IsReusable = 0x0001; + public static final int UseActivation = 0x0002; + public static final int UseBlock = 0x0008; + + public static final int AllowSwapPIN = 0x0010; + public static final int AllowSwapPIN2 = 0x0020; + public static final int UseCVC = 0x0040; + public static final int ForbidDefaultPIN = 0x0080; + + public static final int UseOneCommandAtTime = 0x0100; + public static final int UseNDEF = 0x0200; + public static final int UseDynamicNDEF = 0x0400; + public static final int SmartSecurityDelay = 0x0800; + + public static final int Protocol_AllowUnencrypted = 0x1000; + public static final int Protocol_AllowStaticEncryption = 0x2000; + +} diff --git a/app/src/main/java/com/tangem/cardReader/TLV.java b/app/src/main/java/com/tangem/cardReader/TLV.java index 0a6e398f9e..2bfe7751b8 100644 --- a/app/src/main/java/com/tangem/cardReader/TLV.java +++ b/app/src/main/java/com/tangem/cardReader/TLV.java @@ -1,222 +1,222 @@ -package com.tangem.cardReader; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.Arrays; - -/** - * Created by dvol on 23.06.2017. - */ - -public class TLV { - public enum Tag { - TAG_Unknown(0x00), - TAG_CardID(0x01), - TAG_Status(0x02), - TAG_CardPublicKey(0x03), - TAG_CardSignature(0x04), - TAG_CurveID(0x05), - TAG_HashAlgID(0x06), - TAG_SigningMethod(0x07), - TAG_MaxSignatures(0x08), - TAG_PauseBeforePIN2(0x09), - TAG_SettingsMask(0x0A), - TAG_CardData(0x0C), - TAG_NDEFData(0x0D), - TAG_CreateWalletAtPersonalize(0x0E), - TAG_Health(0x0F), - - TAG_PIN(0x10), - TAG_PIN2(0x11), - TAG_NewPIN(0x12), - TAG_NewPIN2(0x13), - TAG_NewPIN_Hash(0x14), - TAG_NewPIN2_Hash(0x15), - TAG_Challenge(0x16), - TAG_Salt(0x17), - TAG_ValidationCounter(0x18), - TAG_CVC(0x19), - - TAG_Session_Key_A(0x1A), - TAG_Session_Key_B(0x1B), - TAG_Pause(0x1C), - - TAG_Manufacture_ID(0x20), - TAG_Manufacturer_Signature(0x21), - - TAG_Issuer_Data_PublicKey(0x30), - TAG_Issuer_Transaction_PublicKey(0x31), - TAG_Issuer_Data(0x32), - TAG_Issuer_Data_Signature(0x33), - TAG_Issuer_Transaction_Signature(0x34), - - TAG_IsActivated(0x3A), - TAG_ActivationSeed(0x3B), - TAG_ResetPIN(0x36), - - TAG_CodePageAddress(0x40), - TAG_CodePageCount(0x41), - TAG_CodeHash(0x42), - - TAG_TrOut_Hash(0x50), - TAG_TrOut_HashSize(0x51), - TAG_TrOut_Raw(0x52), - - TAG_Wallet_PublicKey(0x60), - TAG_Signature(0x61), - TAG_RemainingSignatures(0x62), - TAG_SignedHashes(0x63), - - TAG_Wallet_PrivateKey(0x70), - TAG_Card_PrivateKey(0x71), - TAG_Block_Reason(0x72), - - - TAG_Firmware(0x80), - TAG_Batch(0x81), - TAG_ManufactureDateTime(0x82), - TAG_Issuer_ID(0x83), - TAG_Blockchain_ID(0x84), - TAG_Manufacturer_PublicKey(0x85), - TAG_CardID_Manufacturer_Signature(0x86), - - TAG_Token_Symbol(0xA0), - TAG_Token_Contract_Address(0xA1), - TAG_Token_Decimal(0xA2), - TAG_Denomination(/*0xC0*/0xee), //TODO: quick fix - TAG_ValidatedBalance(0xC1), - TAG_LastSign_Date(0xC2); - - - Tag(int Code) { - this.Code = Code; - } - - public int getCode() { - return Code; - } - - private int Code; - - public static Tag ByCode(int Code) { - Tag[] allTags = Tag.values(); - for (Tag t : allTags) if (t.getCode() == Code) return t; - return TAG_Unknown; - } - } - - private Tag tag; - - public Tag getTag() { - return tag; - } - - public byte[] Value; - - public TLV(Tag tag, byte[] value) { - this.tag = tag; - this.Value = value; - } - - public void WriteToStream(ByteArrayOutputStream stream) throws IOException { - stream.write(tag.getCode()); - if (Value != null) { - if (Value.length > 0xFE) { - stream.write(0xFF); - stream.write((Value.length >> 8) & 0xFF); - stream.write(Value.length & 0xFF); - } else { - stream.write(Value.length & 0xFF); - } - stream.write(Value); - } else { - stream.write(0x00); - } - } - - public static TLV ReadFromStream(ByteArrayInputStream stream) throws IOException { - int code = stream.read(); - if (code == -1) return null; - int len = stream.read(); - if (len == -1) - throw new IOException("Can't read TLV"); - if (len == 0xFF) { - int lenH = stream.read(); - if (lenH == -1) - throw new IOException("Can't read TLV"); - len = stream.read(); - if (len == -1) - throw new IOException("Can't read TLV"); - len |= (lenH << 8); - } - byte[] value = new byte[len]; - if (len > 0) { - if (len != stream.read(value)) { - throw new IOException("Can't read TLV"); - } - } - Tag tag = Tag.ByCode(code); - TLV result = new TLV(tag, value); - return result; - } - - public int getAsInt() { - return Util.byteArrayToInt(Value); - } - - public String getAsHexString() { - return Util.bytesToHex(Value); - } - - public String getAsString() { - //String s=String.valueOf(Value); - if (Value[Value.length - 1] == 0) { - String s1 = new String(Arrays.copyOfRange(Value, 0, Value.length - 1), Charset.forName("utf-8")); - return s1.trim(); - } else { - String s1 = new String(Value, Charset.forName("utf-8")); - return s1.trim(); - - } - } - - @Override - public String toString() { - switch (tag) { - case TAG_CardData: - case TAG_Issuer_Data: { - try { - TLVList tlvSub = TLVList.fromBytes(Value); - return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), tlvSub.toString()); - } catch (TLVException e) { - e.printStackTrace(); - } - if (Value != null) { - return String.format("%s[%d]: %s (non TLV)", tag.name(), Value.length, Util.bytesToHex(Value)); - } else { - return String.format("%s[]: [[NULL]]", tag.name()); - } - } - case TAG_CurveID: - case TAG_HashAlgID: - case TAG_Blockchain_ID: - case TAG_Manufacture_ID: - case TAG_Firmware: - case TAG_Issuer_ID: - case TAG_Token_Symbol: - if (Value != null) { - return String.format("%s[%d]: %s(%s)", tag.name(), Value.length, Util.bytesToHex(Value), getAsString()); - } else { - return String.format("%s[]: [[NULL]]", tag.name()); - } - default: - if (Value != null) { - return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value)); - } else { - return String.format("%s[]: [[NULL]]", tag.name()); - } - } - } -} +package com.tangem.cardReader; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Arrays; + +/** + * Created by dvol on 23.06.2017. + */ + +public class TLV { + public enum Tag { + TAG_Unknown(0x00), + TAG_CardID(0x01), + TAG_Status(0x02), + TAG_CardPublicKey(0x03), + TAG_CardSignature(0x04), + TAG_CurveID(0x05), + TAG_HashAlgID(0x06), + TAG_SigningMethod(0x07), + TAG_MaxSignatures(0x08), + TAG_PauseBeforePIN2(0x09), + TAG_SettingsMask(0x0A), + TAG_CardData(0x0C), + TAG_NDEFData(0x0D), + TAG_CreateWalletAtPersonalize(0x0E), + TAG_Health(0x0F), + + TAG_PIN(0x10), + TAG_PIN2(0x11), + TAG_NewPIN(0x12), + TAG_NewPIN2(0x13), + TAG_NewPIN_Hash(0x14), + TAG_NewPIN2_Hash(0x15), + TAG_Challenge(0x16), + TAG_Salt(0x17), + TAG_ValidationCounter(0x18), + TAG_CVC(0x19), + + TAG_Session_Key_A(0x1A), + TAG_Session_Key_B(0x1B), + TAG_Pause(0x1C), + + TAG_Manufacture_ID(0x20), + TAG_Manufacturer_Signature(0x21), + + TAG_Issuer_Data_PublicKey(0x30), + TAG_Issuer_Transaction_PublicKey(0x31), + TAG_Issuer_Data(0x32), + TAG_Issuer_Data_Signature(0x33), + TAG_Issuer_Transaction_Signature(0x34), + + TAG_IsActivated(0x3A), + TAG_ActivationSeed(0x3B), + TAG_ResetPIN(0x36), + + TAG_CodePageAddress(0x40), + TAG_CodePageCount(0x41), + TAG_CodeHash(0x42), + + TAG_TrOut_Hash(0x50), + TAG_TrOut_HashSize(0x51), + TAG_TrOut_Raw(0x52), + + TAG_Wallet_PublicKey(0x60), + TAG_Signature(0x61), + TAG_RemainingSignatures(0x62), + TAG_SignedHashes(0x63), + + TAG_Wallet_PrivateKey(0x70), + TAG_Card_PrivateKey(0x71), + TAG_Block_Reason(0x72), + + + TAG_Firmware(0x80), + TAG_Batch(0x81), + TAG_ManufactureDateTime(0x82), + TAG_Issuer_ID(0x83), + TAG_Blockchain_ID(0x84), + TAG_Manufacturer_PublicKey(0x85), + TAG_CardID_Manufacturer_Signature(0x86), + + TAG_Token_Symbol(0xA0), + TAG_Token_Contract_Address(0xA1), + TAG_Token_Decimal(0xA2), + TAG_Denomination(/*0xC0*/0xee), //TODO: quick fix + TAG_ValidatedBalance(0xC1), + TAG_LastSign_Date(0xC2); + + + Tag(int Code) { + this.Code = Code; + } + + public int getCode() { + return Code; + } + + private int Code; + + public static Tag ByCode(int Code) { + Tag[] allTags = Tag.values(); + for (Tag t : allTags) if (t.getCode() == Code) return t; + return TAG_Unknown; + } + } + + private Tag tag; + + public Tag getTag() { + return tag; + } + + public byte[] Value; + + public TLV(Tag tag, byte[] value) { + this.tag = tag; + this.Value = value; + } + + public void WriteToStream(ByteArrayOutputStream stream) throws IOException { + stream.write(tag.getCode()); + if (Value != null) { + if (Value.length > 0xFE) { + stream.write(0xFF); + stream.write((Value.length >> 8) & 0xFF); + stream.write(Value.length & 0xFF); + } else { + stream.write(Value.length & 0xFF); + } + stream.write(Value); + } else { + stream.write(0x00); + } + } + + public static TLV ReadFromStream(ByteArrayInputStream stream) throws IOException { + int code = stream.read(); + if (code == -1) return null; + int len = stream.read(); + if (len == -1) + throw new IOException("Can't read TLV"); + if (len == 0xFF) { + int lenH = stream.read(); + if (lenH == -1) + throw new IOException("Can't read TLV"); + len = stream.read(); + if (len == -1) + throw new IOException("Can't read TLV"); + len |= (lenH << 8); + } + byte[] value = new byte[len]; + if (len > 0) { + if (len != stream.read(value)) { + throw new IOException("Can't read TLV"); + } + } + Tag tag = Tag.ByCode(code); + TLV result = new TLV(tag, value); + return result; + } + + public int getAsInt() { + return Util.byteArrayToInt(Value); + } + + public String getAsHexString() { + return Util.bytesToHex(Value); + } + + public String getAsString() { + //String s=String.valueOf(Value); + if (Value[Value.length - 1] == 0) { + String s1 = new String(Arrays.copyOfRange(Value, 0, Value.length - 1), Charset.forName("utf-8")); + return s1.trim(); + } else { + String s1 = new String(Value, Charset.forName("utf-8")); + return s1.trim(); + + } + } + + @Override + public String toString() { + switch (tag) { + case TAG_CardData: + case TAG_Issuer_Data: { + try { + TLVList tlvSub = TLVList.fromBytes(Value); + return String.format("%s[%d]: %s (%s)", tag.name(), Value.length, Util.bytesToHex(Value), tlvSub.toString()); + } catch (TLVException e) { + e.printStackTrace(); + } + if (Value != null) { + return String.format("%s[%d]: %s (non TLV)", tag.name(), Value.length, Util.bytesToHex(Value)); + } else { + return String.format("%s[]: [[NULL]]", tag.name()); + } + } + case TAG_CurveID: + case TAG_HashAlgID: + case TAG_Blockchain_ID: + case TAG_Manufacture_ID: + case TAG_Firmware: + case TAG_Issuer_ID: + case TAG_Token_Symbol: + if (Value != null) { + return String.format("%s[%d]: %s(%s)", tag.name(), Value.length, Util.bytesToHex(Value), getAsString()); + } else { + return String.format("%s[]: [[NULL]]", tag.name()); + } + default: + if (Value != null) { + return String.format("%s[%d]: %s", tag.name(), Value.length, Util.bytesToHex(Value)); + } else { + return String.format("%s[]: [[NULL]]", tag.name()); + } + } + } +} diff --git a/app/src/main/java/com/tangem/cardReader/TLVException.java b/app/src/main/java/com/tangem/cardReader/TLVException.java index a213dd71aa..db5588dee1 100644 --- a/app/src/main/java/com/tangem/cardReader/TLVException.java +++ b/app/src/main/java/com/tangem/cardReader/TLVException.java @@ -1,18 +1,18 @@ -package com.tangem.cardReader; - -public class TLVException extends Exception { - - private static final long serialVersionUID = 1L; - - public TLVException(String message){ - super(message); - } - - public TLVException(String message, Throwable cause) { - super(message, cause); - } - - public TLVException(Throwable cause) { - super(cause); - } +package com.tangem.cardReader; + +public class TLVException extends Exception { + + private static final long serialVersionUID = 1L; + + public TLVException(String message){ + super(message); + } + + public TLVException(String message, Throwable cause) { + super(message, cause); + } + + public TLVException(Throwable cause) { + super(cause); + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/cardReader/TLVList.java b/app/src/main/java/com/tangem/cardReader/TLVList.java index 59ba0fdc2f..ed9686de0a 100644 --- a/app/src/main/java/com/tangem/cardReader/TLVList.java +++ b/app/src/main/java/com/tangem/cardReader/TLVList.java @@ -1,72 +1,72 @@ -package com.tangem.cardReader; - -/** - * Created by dvol on 23.06.2017. - */ - -import android.support.annotation.NonNull; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; - -public class TLVList extends ArrayList { - public String getParsedTLVs(String Prefix) { - String parsed = ""; - for (int i = 0; i < size(); i++) { - parsed += Prefix + this.get(i).toString() + (i < size() - 1 ? "\n" : ""); - } - return parsed;//.substring(0,parsed.length()-2); - } - - public TLVList() { - super(); - } - - public TLVList(@NonNull Collection c) { - super(c); - } - - public TLV getTLV(TLV.Tag tag) { - for (TLV tlv : this) { - if (tlv.getTag() == tag) return tlv; - } - return null; - } - - public int getTagAsInt(TLV.Tag tag) { - TLV tlv = getTLV(tag); - return Util.byteArrayToInt(tlv.Value); - } - - public byte[] toBytes() { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - for (TLV tlv : this) { - try { - tlv.WriteToStream(stream); - } catch (IOException e) { - e.printStackTrace(); - break; - } - } - return stream.toByteArray(); - } - - public static TLVList fromBytes(byte[] mData) throws TLVException { - TLVList tlvList = new TLVList(); - ByteArrayInputStream stream = new ByteArrayInputStream(mData); - TLV tlv = null; - do { - try { - tlv = TLV.ReadFromStream(stream); - if (tlv != null) tlvList.add(tlv); - } catch (IOException e) { - throw new TLVException("TLVError: " + e.getMessage()); - } - } - while (tlv != null); - return tlvList; - } -} +package com.tangem.cardReader; + +/** + * Created by dvol on 23.06.2017. + */ + +import android.support.annotation.NonNull; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; + +public class TLVList extends ArrayList { + public String getParsedTLVs(String Prefix) { + String parsed = ""; + for (int i = 0; i < size(); i++) { + parsed += Prefix + this.get(i).toString() + (i < size() - 1 ? "\n" : ""); + } + return parsed;//.substring(0,parsed.length()-2); + } + + public TLVList() { + super(); + } + + public TLVList(@NonNull Collection c) { + super(c); + } + + public TLV getTLV(TLV.Tag tag) { + for (TLV tlv : this) { + if (tlv.getTag() == tag) return tlv; + } + return null; + } + + public int getTagAsInt(TLV.Tag tag) { + TLV tlv = getTLV(tag); + return Util.byteArrayToInt(tlv.Value); + } + + public byte[] toBytes() { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + for (TLV tlv : this) { + try { + tlv.WriteToStream(stream); + } catch (IOException e) { + e.printStackTrace(); + break; + } + } + return stream.toByteArray(); + } + + public static TLVList fromBytes(byte[] mData) throws TLVException { + TLVList tlvList = new TLVList(); + ByteArrayInputStream stream = new ByteArrayInputStream(mData); + TLV tlv = null; + do { + try { + tlv = TLV.ReadFromStream(stream); + if (tlv != null) tlvList.add(tlv); + } catch (IOException e) { + throw new TLVException("TLVError: " + e.getMessage()); + } + } + while (tlv != null); + return tlvList; + } +} diff --git a/app/src/main/java/com/tangem/wallet/BTCUtils.java b/app/src/main/java/com/tangem/wallet/BTCUtils.java index 1f7986049a..1f298fe57b 100644 --- a/app/src/main/java/com/tangem/wallet/BTCUtils.java +++ b/app/src/main/java/com/tangem/wallet/BTCUtils.java @@ -1,519 +1,519 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 29.09.2017. - */ - -import android.util.Log; - -import com.tangem.cardReader.Util; - -import org.spongycastle.asn1.ASN1EncodableVector; -import org.spongycastle.asn1.ASN1Integer; -import org.spongycastle.asn1.DERSequence; -import org.spongycastle.asn1.DERSequenceGenerator; -import org.spongycastle.asn1.sec.SECNamedCurves; -import org.spongycastle.asn1.x9.X9ECParameters; -import org.spongycastle.asn1.x9.X9IntegerConverter; -import org.spongycastle.crypto.params.ECDomainParameters; -import org.spongycastle.crypto.params.ECPrivateKeyParameters; -import org.spongycastle.crypto.params.ECPublicKeyParameters; -import org.spongycastle.crypto.signers.ECDSASigner; -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; -import org.spongycastle.jce.spec.ECPrivateKeySpec; -import org.spongycastle.jce.spec.ECPublicKeySpec; -import org.spongycastle.math.ec.ECAlgorithms; -import org.spongycastle.math.ec.ECCurve; -import org.spongycastle.math.ec.ECPoint; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.PublicKey; -import java.security.Signature; -import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; -import java.text.Format; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Stack; -import java.util.regex.Pattern; - -import static org.bitcoinj.core.ECKey.CURVE; -import static org.bitcoinj.core.ECKey.HALF_CURVE_ORDER; - -@SuppressWarnings({"WeakerAccess", "TryWithIdenticalCatches", "unused"}) -public final class BTCUtils { - static final BigInteger LARGEST_PRIVATE_KEY = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);//SECP256K1_N - public static final long MIN_FEE_PER_KB = 10000; - public static final long MAX_ALLOWED_FEE = FormatUtil.parseValue("0.1"); - public static final long MIN_PRIORITY_FOR_NO_FEE = 57600000; - public static final long MIN_MIN_OUTPUT_VALUE_FOR_NO_FEE = 10000000L; - public static final int MAX_TX_LEN_FOR_NO_FEE = 10000; - public static final float EXPECTED_BLOCKS_PER_DAY = 144.0f;//(expected confirmations per day) - - public static long calcMinimumFee(int txLen, Collection unspentOutputInfos, long minOutput) { - if (isZeroFeeAllowed(txLen, unspentOutputInfos, minOutput)) { - return 0; - } - return MIN_FEE_PER_KB * (1 + txLen / 1000); - } - - public static boolean isZeroFeeAllowed(int txLen, Collection unspentOutputInfos, long minOutput) { - if (txLen < MAX_TX_LEN_FOR_NO_FEE && minOutput > MIN_MIN_OUTPUT_VALUE_FOR_NO_FEE) { - long priority = 0; - for (UnspentOutputInfo output : unspentOutputInfos) { - if (output.confirmations > 0) { - priority += output.confirmations * output.value; - } - } - priority /= txLen; - if (priority > MIN_PRIORITY_FOR_NO_FEE) { - return true; - } - } - return false; - } - - public static int getMaximumTxSize(Collection unspentOutputInfos, int outputsCount, boolean compressedPublicKey) throws BitcoinException { - if (unspentOutputInfos == null || unspentOutputInfos.isEmpty()) { - throw new BitcoinException(BitcoinException.ERR_NO_INPUT, "No information about tx inputs provided"); - } - int maxInputScriptLen = 73 + (compressedPublicKey ? 33 : 65); - return 9 + unspentOutputInfos.size() * (41 + maxInputScriptLen) + outputsCount * 33; - } - - public static String publicKeyToAddress(byte[] publicKey) { - return publicKeyToAddress(false, publicKey); - } - - public static String publicKeyToAddress(boolean testNet, byte[] publicKey) { - try { - byte[] hashedPublicKey = CryptoUtil.sha256ripemd160(publicKey); - byte[] addressBytes = new byte[1 + hashedPublicKey.length + 4]; - addressBytes[0] = (byte) (testNet ? 111 : 0); - System.arraycopy(hashedPublicKey, 0, addressBytes, 1, hashedPublicKey.length); - MessageDigest digestSha = MessageDigest.getInstance("SHA-256"); - digestSha.update(addressBytes, 0, addressBytes.length - 4); - byte[] check = digestSha.digest(digestSha.digest()); - System.arraycopy(check, 0, addressBytes, hashedPublicKey.length + 1, 4); - return Base58.encodeBase58(addressBytes); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - public static String toHex(byte[] bytes) { - if (bytes == null) { - return ""; - } - final char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - char[] hexChars = new char[bytes.length * 2]; - int v; - for (int j = 0; j < bytes.length; j++) { - v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); - } - - - public static byte[] buildTXForSign(String myAddress, String outputAddress, String changeAddress, ArrayList unspentOutputs, int currentInputPos, long amount, long change) throws BitcoinException, IOException { - byte[] myScript = Transaction.Script.buildOutput(myAddress).bytes; - unspentOutputs.get(currentInputPos).scriptForBuild = myScript; - int inputPos = currentInputPos; - byte[] body = buildBodyTX(outputAddress, changeAddress, unspentOutputs, inputPos, amount, change); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - os.write(body); - os.write(new byte[]{0x01, 0x00, 0x00, 0x00}); - byte[] tx = os.toByteArray(); - return tx; - } - - public static byte[] buildTXForSend(String outputAddress, String changeAddress, ArrayList unspentOutputs, long amount, long change) throws BitcoinException, IOException { - int inputPos = -1; - byte[] body = buildBodyTX(outputAddress, changeAddress, unspentOutputs, inputPos, amount, change); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - os.write(body); - byte[] tx = os.toByteArray(); - return tx; - } - - public static byte[] buildBodyTX(String outputAddress, String changeAddress, ArrayList unspentOutputs, int inputPos, long amount, long change) throws BitcoinException, IOException { - - //0200000000 - BitcoinOutputStream forSign = new BitcoinOutputStream(); - forSign.writeInt32(0x01);//write(new byte[]{0x02, 0x00, 0x00, 0x00}); // version - - //01 - byte inputCount = (byte)unspentOutputs.size(); - forSign.write(inputCount); // input count - //hex str hash prev btc - - for(int i = 0; i < inputCount; ++i) - { - UnspentOutputInfo outPut = unspentOutputs.get(i); - int outputIndex = outPut.outputIndex; - byte[] txHash = BTCUtils.reverse(Util.hexToBytes(outPut.txHashForBuild));//Sha256Hash.hash(rawTxByte); - forSign.write(txHash); - forSign.writeInt32(outputIndex); //output index in prev tx - if(inputPos ==-1 || i == inputPos) - { - // hex str 1976a914....88ac - forSign.write((byte)outPut.scriptForBuild.length); - forSign.write(outPut.scriptForBuild); - } - else - { - forSign.write(0x00); - } - //ffffffff - forSign.write(new byte[]{(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}); // sequence - } - - - //02 - byte outputCount = (byte)((change==0) ? 1 : 2); // outputCount - forSign.write(outputCount); - - //8 bytes - - forSign.writeInt64(amount); //amount - byte[] sendScript = Transaction.Script.buildOutput(outputAddress).bytes; // build out - //hex str 1976a914....88ac - forSign.write((byte)sendScript.length); - forSign.write(sendScript); - - if(change!=0){ - //8 bytes - forSign.writeInt64(change); // change - //hex str 1976a914....88ac - byte[] chancheScript = Transaction.Script.buildOutput(changeAddress).bytes; //build out - forSign.write((byte)chancheScript.length); - forSign.write(chancheScript); - - } - - //00000000 - forSign.write(new byte[]{0x00, 0x00, 0x00, 0x00}); - - //forSign.write(new byte[]{0x01, 0x00, 0x00, 0x00}); - - byte[] rawData = forSign.toByteArray(); - - Log.e("Sign_TX_Body", BTCUtils.toHex(rawData)); - - return rawData; - } - - int calculateSize(int inputCount, int outputCount) - { - int size = 0; - size += 4; // header - size += 1; //inputCount - - //hex str hash prev btc - - for(int i = 0; i < inputCount; ++i) - { - size += 32; //prevtx - size += 4; //outputIndex; - size += 1; //scriptLength - // size+=script; - size += 4; //ffffffff - } - - size+=1; //outputCount - size+=8; //amount - size+=1; - // size+=script; - - if(outputCount > 1) - { - size+=8; - size+=1; - //scriptLen; - } - - size+=4; - return size; - } - - - public static byte[] buildBodyTX(String outputAddress, String changeAddress, int outputIndex, String prevID, long amount, long change, byte[] script) throws BitcoinException, IOException { - - //0200000000 - BitcoinOutputStream forSign = new BitcoinOutputStream(); - forSign.writeInt32(0x01);//write(new byte[]{0x02, 0x00, 0x00, 0x00}); // version - - //01 - byte inputCount = 1; - forSign.write(inputCount); // input count - //hex str hash prev btc - byte[] txHash = BTCUtils.reverse(Util.hexToBytes(prevID));//Sha256Hash.hash(rawTxByte); - forSign.write(txHash); //previos tx hash - - //00000000 - //byte indexOutput = outputIndex; - forSign.writeInt32(outputIndex/*indexOutput*/); //output index in prev tx - //forSign.write(0x00); - - // hex str 1976a914....88ac - forSign.write((byte)script.length); - forSign.write(script); - - //ffffffff - forSign.write(new byte[]{(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}); // sequence - - //02 - byte outputCount = (byte)((change==0) ? 1 : 2); // outputCount - forSign.write(outputCount); - - //8 bytes - - forSign.writeInt64(amount); //amount - byte[] sendScript = Transaction.Script.buildOutput(outputAddress).bytes; // build out - //hex str 1976a914....88ac - forSign.write((byte)sendScript.length); - forSign.write(sendScript); - - if(change!=0){ - //8 bytes - forSign.writeInt64(change); // change - //hex str 1976a914....88ac - byte[] chancheScript = Transaction.Script.buildOutput(changeAddress).bytes; //build out - forSign.write((byte)chancheScript.length); - forSign.write(chancheScript); - - } - - //00000000 - forSign.write(new byte[]{0x00, 0x00, 0x00, 0x00}); - - byte[] rawData = forSign.toByteArray(); - - //Log.e("Sign_TX_Body", BTCUtils.toHex(rawData)); - return rawData; - } - - public static ArrayList getPrevTX(String hex) throws BitcoinException { - byte[] rawTxByte = fromHex(hex); - Transaction baseTx = new Transaction(rawTxByte); - ArrayList prevHashes = new ArrayList(); - for(int i =0; i < baseTx.inputs.length; ++i) - { - Transaction.Input input = baseTx.inputs[i]; - prevHashes.add(input.outPoint.hash); - } - return prevHashes; - } - - public static boolean isInput(String myAddress, String hex) throws BitcoinException { - byte[] rawTxByte = fromHex(hex); - Transaction baseTx = new Transaction(rawTxByte); - byte[] myScript = Transaction.Script.buildOutput(myAddress).bytes; - for(int i =0; i < baseTx.inputs.length; ++i) - { - Transaction.Input input = baseTx.inputs[i]; - byte[] script = input.script.bytes; - - // find outputs - if (Arrays.equals(myScript, script)){ - return true; - } - } - return false; - } - public static ArrayList getOutputs(List rawTxList, byte[] outputScriptWeAreAbleToSpend) throws BitcoinException { - ArrayList unspentOutputs = new ArrayList<>(); - - for(Tangem_Card.UnspentTransaction current: rawTxList) - { - byte[] rawTxByte = BTCUtils.fromHex(current.Raw); - if (rawTxByte == null) - { - continue; - } - - Transaction baseTx = new Transaction(rawTxByte); - - if(baseTx.inputs.length == 0 || baseTx.outputs.length == 0) - throw new IllegalArgumentException("Unable to decode given transaction"); - - byte[] txHash = BTCUtils.reverse(CryptoUtil.doubleSha256(rawTxByte)); - String txHashForBuild = current.txID; - byte[] sign = null; - - for (int outputIndex = 0; outputIndex < baseTx.outputs.length; outputIndex++) { - Transaction.Output output = baseTx.outputs[outputIndex]; - - // find outputs - if (Arrays.equals(outputScriptWeAreAbleToSpend, output.script.bytes)) { - unspentOutputs.add(new UnspentOutputInfo(txHash, output.script, output.value, outputIndex, -1, txHashForBuild, sign)); - } - } - - } - - return unspentOutputs; - } - - public static byte[] fromHex(String s) { - if (s != null) { - try { - StringBuilder sb = new StringBuilder(s.length()); - for (int i = 0; i < s.length(); i++) { - char ch = s.charAt(i); - if (!Character.isWhitespace(ch)) { - sb.append(ch); - } - } - s = sb.toString(); - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - int hi = (Character.digit(s.charAt(i), 16) << 4); - int low = Character.digit(s.charAt(i + 1), 16); - if (hi >= 256 || low < 0 || low >= 16) { - return null; - } - data[i / 2] = (byte) (hi | low); - } - return data; - } catch (Exception ignored) { - } - } - return null; - } - - public static byte[] reverse(byte[] bytes) { - byte[] result = new byte[bytes.length]; - for (int i = 0; i < bytes.length; i++) { - result[i] = bytes[bytes.length - i - 1]; - } - return result; - } - - public static byte[] reverseInPlace(byte[] bytes) { - int len = bytes.length / 2; - for (int i = 0; i < len; i++) { - byte t = bytes[i]; - bytes[i] = bytes[bytes.length - i - 1]; - bytes[bytes.length - i - 1] = t; - } - return bytes; - } - - public static int findSpendableOutput(Transaction tx, String forAddress, long minAmount) throws BitcoinException { - byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(forAddress).bytes; - int indexOfOutputToSpend = -1; - for (int indexOfOutput = 0; indexOfOutput < tx.outputs.length; indexOfOutput++) { - Transaction.Output output = tx.outputs[indexOfOutput]; - if (Arrays.equals(outputScriptWeAreAbleToSpend, output.script.bytes)) { - indexOfOutputToSpend = indexOfOutput; - break;//only one input is supported for now - } - } - if (indexOfOutputToSpend == -1) { - throw new BitcoinException(BitcoinException.ERR_NO_SPENDABLE_OUTPUTS_FOR_THE_ADDRESS, "No spendable standard outputs for " + forAddress + " have found", forAddress); - } - final long spendableOutputValue = tx.outputs[indexOfOutputToSpend].value; - if (spendableOutputValue < minAmount) { - throw new BitcoinException(BitcoinException.ERR_INSUFFICIENT_FUNDS, "Unspent amount is too small: " + spendableOutputValue, spendableOutputValue); - } - return indexOfOutputToSpend; - } - - public static void verify(Transaction.Script[] scripts, Transaction spendTx) throws Transaction.Script.ScriptInvalidException { - for (int i = 0; i < scripts.length; i++) { - Stack stack = new Stack<>(); - spendTx.inputs[i].script.run(stack);//load signature+public key - scripts[i].run(i, spendTx, stack); //verify that this transaction able to spend that output - if (Transaction.Script.verifyFails(stack)) { - throw new Transaction.Script.ScriptInvalidException("Signature is invalid"); - } - } - } - - public static class FeeChangeAndSelectedOutputs { - public final long amountForRecipient, change, fee; - public final ArrayList outputsToSpend; - - public FeeChangeAndSelectedOutputs(long fee, long change, long amountForRecipient, ArrayList outputsToSpend) { - this.fee = fee; - this.change = change; - this.amountForRecipient = amountForRecipient; - this.outputsToSpend = outputsToSpend; - } - } - - public static FeeChangeAndSelectedOutputs calcFeeChangeAndSelectOutputsToSpend(List unspentOutputs, long amountToSend, long extraFee, final boolean isPublicKeyCompressed) throws BitcoinException { - long fee = 0;//calculated below - long change = 0; - long valueOfUnspentOutputs; - ArrayList outputsToSpend = new ArrayList<>(); - if (amountToSend <= 0) { - //transfer all funds from these addresses to outputAddress - change = 0; - valueOfUnspentOutputs = 0; - for (UnspentOutputInfo outputInfo : unspentOutputs) { - outputsToSpend.add(outputInfo); - valueOfUnspentOutputs += outputInfo.value; - } - final int txLen = BTCUtils.getMaximumTxSize(unspentOutputs, 1, isPublicKeyCompressed); - fee = BTCUtils.calcMinimumFee(txLen, unspentOutputs, valueOfUnspentOutputs - MIN_FEE_PER_KB * (1 + txLen / 1000)); - amountToSend = valueOfUnspentOutputs - fee - extraFee; - } else { - valueOfUnspentOutputs = 0; - for (UnspentOutputInfo outputInfo : unspentOutputs) { - outputsToSpend.add(outputInfo); - valueOfUnspentOutputs += outputInfo.value; - long updatedFee = MIN_FEE_PER_KB; - for (int i = 0; i < 3; i++) { - fee = updatedFee; - change = valueOfUnspentOutputs - fee - extraFee - amountToSend; - final int txLen = BTCUtils.getMaximumTxSize(unspentOutputs, change > 0 ? 2 : 1, isPublicKeyCompressed); - updatedFee = BTCUtils.calcMinimumFee(txLen, unspentOutputs, change > 0 ? Math.min(amountToSend, change) : amountToSend); - if (updatedFee == fee) { - break; - } - } - fee = updatedFee; - if (valueOfUnspentOutputs >= amountToSend + fee + extraFee) { - break; - } - } - - } - if (amountToSend > valueOfUnspentOutputs - fee) { - throw new BitcoinException(BitcoinException.ERR_INSUFFICIENT_FUNDS, "Not enough funds", valueOfUnspentOutputs - fee); - } - if (outputsToSpend.isEmpty()) { - throw new BitcoinException(BitcoinException.ERR_NO_INPUT, "No outputs to spend"); - } - if (fee + extraFee > MAX_ALLOWED_FEE) { - throw new BitcoinException(BitcoinException.ERR_FEE_IS_TOO_BIG, "Fee is too big", fee); - } - if (fee < 0 || extraFee < 0) { - throw new BitcoinException(BitcoinException.ERR_FEE_IS_LESS_THEN_ZERO, "Incorrect fee", fee); - } - if (change < 0) { - throw new BitcoinException(BitcoinException.ERR_CHANGE_IS_LESS_THEN_ZERO, "Incorrect change", change); - } - if (amountToSend < 0) { - throw new BitcoinException(BitcoinException.ERR_AMOUNT_TO_SEND_IS_LESS_THEN_ZERO, "Incorrect amount to send", amountToSend); - } - return new FeeChangeAndSelectedOutputs(fee + extraFee, change, amountToSend, outputsToSpend); - - } -} +package com.tangem.wallet; + +/** + * Created by Ilia on 29.09.2017. + */ + +import android.util.Log; + +import com.tangem.cardReader.Util; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.DERSequenceGenerator; +import org.spongycastle.asn1.sec.SECNamedCurves; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9IntegerConverter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.signers.ECDSASigner; +import org.spongycastle.jce.ECNamedCurveTable; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; +import org.spongycastle.math.ec.ECAlgorithms; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.text.Format; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Stack; +import java.util.regex.Pattern; + +import static org.bitcoinj.core.ECKey.CURVE; +import static org.bitcoinj.core.ECKey.HALF_CURVE_ORDER; + +@SuppressWarnings({"WeakerAccess", "TryWithIdenticalCatches", "unused"}) +public final class BTCUtils { + static final BigInteger LARGEST_PRIVATE_KEY = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16);//SECP256K1_N + public static final long MIN_FEE_PER_KB = 10000; + public static final long MAX_ALLOWED_FEE = FormatUtil.parseValue("0.1"); + public static final long MIN_PRIORITY_FOR_NO_FEE = 57600000; + public static final long MIN_MIN_OUTPUT_VALUE_FOR_NO_FEE = 10000000L; + public static final int MAX_TX_LEN_FOR_NO_FEE = 10000; + public static final float EXPECTED_BLOCKS_PER_DAY = 144.0f;//(expected confirmations per day) + + public static long calcMinimumFee(int txLen, Collection unspentOutputInfos, long minOutput) { + if (isZeroFeeAllowed(txLen, unspentOutputInfos, minOutput)) { + return 0; + } + return MIN_FEE_PER_KB * (1 + txLen / 1000); + } + + public static boolean isZeroFeeAllowed(int txLen, Collection unspentOutputInfos, long minOutput) { + if (txLen < MAX_TX_LEN_FOR_NO_FEE && minOutput > MIN_MIN_OUTPUT_VALUE_FOR_NO_FEE) { + long priority = 0; + for (UnspentOutputInfo output : unspentOutputInfos) { + if (output.confirmations > 0) { + priority += output.confirmations * output.value; + } + } + priority /= txLen; + if (priority > MIN_PRIORITY_FOR_NO_FEE) { + return true; + } + } + return false; + } + + public static int getMaximumTxSize(Collection unspentOutputInfos, int outputsCount, boolean compressedPublicKey) throws BitcoinException { + if (unspentOutputInfos == null || unspentOutputInfos.isEmpty()) { + throw new BitcoinException(BitcoinException.ERR_NO_INPUT, "No information about tx inputs provided"); + } + int maxInputScriptLen = 73 + (compressedPublicKey ? 33 : 65); + return 9 + unspentOutputInfos.size() * (41 + maxInputScriptLen) + outputsCount * 33; + } + + public static String publicKeyToAddress(byte[] publicKey) { + return publicKeyToAddress(false, publicKey); + } + + public static String publicKeyToAddress(boolean testNet, byte[] publicKey) { + try { + byte[] hashedPublicKey = CryptoUtil.sha256ripemd160(publicKey); + byte[] addressBytes = new byte[1 + hashedPublicKey.length + 4]; + addressBytes[0] = (byte) (testNet ? 111 : 0); + System.arraycopy(hashedPublicKey, 0, addressBytes, 1, hashedPublicKey.length); + MessageDigest digestSha = MessageDigest.getInstance("SHA-256"); + digestSha.update(addressBytes, 0, addressBytes.length - 4); + byte[] check = digestSha.digest(digestSha.digest()); + System.arraycopy(check, 0, addressBytes, hashedPublicKey.length + 1, 4); + return Base58.encodeBase58(addressBytes); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + public static String toHex(byte[] bytes) { + if (bytes == null) { + return ""; + } + final char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + char[] hexChars = new char[bytes.length * 2]; + int v; + for (int j = 0; j < bytes.length; j++) { + v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + + public static byte[] buildTXForSign(String myAddress, String outputAddress, String changeAddress, ArrayList unspentOutputs, int currentInputPos, long amount, long change) throws BitcoinException, IOException { + byte[] myScript = Transaction.Script.buildOutput(myAddress).bytes; + unspentOutputs.get(currentInputPos).scriptForBuild = myScript; + int inputPos = currentInputPos; + byte[] body = buildBodyTX(outputAddress, changeAddress, unspentOutputs, inputPos, amount, change); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write(body); + os.write(new byte[]{0x01, 0x00, 0x00, 0x00}); + byte[] tx = os.toByteArray(); + return tx; + } + + public static byte[] buildTXForSend(String outputAddress, String changeAddress, ArrayList unspentOutputs, long amount, long change) throws BitcoinException, IOException { + int inputPos = -1; + byte[] body = buildBodyTX(outputAddress, changeAddress, unspentOutputs, inputPos, amount, change); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write(body); + byte[] tx = os.toByteArray(); + return tx; + } + + public static byte[] buildBodyTX(String outputAddress, String changeAddress, ArrayList unspentOutputs, int inputPos, long amount, long change) throws BitcoinException, IOException { + + //0200000000 + BitcoinOutputStream forSign = new BitcoinOutputStream(); + forSign.writeInt32(0x01);//write(new byte[]{0x02, 0x00, 0x00, 0x00}); // version + + //01 + byte inputCount = (byte)unspentOutputs.size(); + forSign.write(inputCount); // input count + //hex str hash prev btc + + for(int i = 0; i < inputCount; ++i) + { + UnspentOutputInfo outPut = unspentOutputs.get(i); + int outputIndex = outPut.outputIndex; + byte[] txHash = BTCUtils.reverse(Util.hexToBytes(outPut.txHashForBuild));//Sha256Hash.hash(rawTxByte); + forSign.write(txHash); + forSign.writeInt32(outputIndex); //output index in prev tx + if(inputPos ==-1 || i == inputPos) + { + // hex str 1976a914....88ac + forSign.write((byte)outPut.scriptForBuild.length); + forSign.write(outPut.scriptForBuild); + } + else + { + forSign.write(0x00); + } + //ffffffff + forSign.write(new byte[]{(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}); // sequence + } + + + //02 + byte outputCount = (byte)((change==0) ? 1 : 2); // outputCount + forSign.write(outputCount); + + //8 bytes + + forSign.writeInt64(amount); //amount + byte[] sendScript = Transaction.Script.buildOutput(outputAddress).bytes; // build out + //hex str 1976a914....88ac + forSign.write((byte)sendScript.length); + forSign.write(sendScript); + + if(change!=0){ + //8 bytes + forSign.writeInt64(change); // change + //hex str 1976a914....88ac + byte[] chancheScript = Transaction.Script.buildOutput(changeAddress).bytes; //build out + forSign.write((byte)chancheScript.length); + forSign.write(chancheScript); + + } + + //00000000 + forSign.write(new byte[]{0x00, 0x00, 0x00, 0x00}); + + //forSign.write(new byte[]{0x01, 0x00, 0x00, 0x00}); + + byte[] rawData = forSign.toByteArray(); + + Log.e("Sign_TX_Body", BTCUtils.toHex(rawData)); + + return rawData; + } + + int calculateSize(int inputCount, int outputCount) + { + int size = 0; + size += 4; // header + size += 1; //inputCount + + //hex str hash prev btc + + for(int i = 0; i < inputCount; ++i) + { + size += 32; //prevtx + size += 4; //outputIndex; + size += 1; //scriptLength + // size+=script; + size += 4; //ffffffff + } + + size+=1; //outputCount + size+=8; //amount + size+=1; + // size+=script; + + if(outputCount > 1) + { + size+=8; + size+=1; + //scriptLen; + } + + size+=4; + return size; + } + + + public static byte[] buildBodyTX(String outputAddress, String changeAddress, int outputIndex, String prevID, long amount, long change, byte[] script) throws BitcoinException, IOException { + + //0200000000 + BitcoinOutputStream forSign = new BitcoinOutputStream(); + forSign.writeInt32(0x01);//write(new byte[]{0x02, 0x00, 0x00, 0x00}); // version + + //01 + byte inputCount = 1; + forSign.write(inputCount); // input count + //hex str hash prev btc + byte[] txHash = BTCUtils.reverse(Util.hexToBytes(prevID));//Sha256Hash.hash(rawTxByte); + forSign.write(txHash); //previos tx hash + + //00000000 + //byte indexOutput = outputIndex; + forSign.writeInt32(outputIndex/*indexOutput*/); //output index in prev tx + //forSign.write(0x00); + + // hex str 1976a914....88ac + forSign.write((byte)script.length); + forSign.write(script); + + //ffffffff + forSign.write(new byte[]{(byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff}); // sequence + + //02 + byte outputCount = (byte)((change==0) ? 1 : 2); // outputCount + forSign.write(outputCount); + + //8 bytes + + forSign.writeInt64(amount); //amount + byte[] sendScript = Transaction.Script.buildOutput(outputAddress).bytes; // build out + //hex str 1976a914....88ac + forSign.write((byte)sendScript.length); + forSign.write(sendScript); + + if(change!=0){ + //8 bytes + forSign.writeInt64(change); // change + //hex str 1976a914....88ac + byte[] chancheScript = Transaction.Script.buildOutput(changeAddress).bytes; //build out + forSign.write((byte)chancheScript.length); + forSign.write(chancheScript); + + } + + //00000000 + forSign.write(new byte[]{0x00, 0x00, 0x00, 0x00}); + + byte[] rawData = forSign.toByteArray(); + + //Log.e("Sign_TX_Body", BTCUtils.toHex(rawData)); + return rawData; + } + + public static ArrayList getPrevTX(String hex) throws BitcoinException { + byte[] rawTxByte = fromHex(hex); + Transaction baseTx = new Transaction(rawTxByte); + ArrayList prevHashes = new ArrayList(); + for(int i =0; i < baseTx.inputs.length; ++i) + { + Transaction.Input input = baseTx.inputs[i]; + prevHashes.add(input.outPoint.hash); + } + return prevHashes; + } + + public static boolean isInput(String myAddress, String hex) throws BitcoinException { + byte[] rawTxByte = fromHex(hex); + Transaction baseTx = new Transaction(rawTxByte); + byte[] myScript = Transaction.Script.buildOutput(myAddress).bytes; + for(int i =0; i < baseTx.inputs.length; ++i) + { + Transaction.Input input = baseTx.inputs[i]; + byte[] script = input.script.bytes; + + // find outputs + if (Arrays.equals(myScript, script)){ + return true; + } + } + return false; + } + public static ArrayList getOutputs(List rawTxList, byte[] outputScriptWeAreAbleToSpend) throws BitcoinException { + ArrayList unspentOutputs = new ArrayList<>(); + + for(Tangem_Card.UnspentTransaction current: rawTxList) + { + byte[] rawTxByte = BTCUtils.fromHex(current.Raw); + if (rawTxByte == null) + { + continue; + } + + Transaction baseTx = new Transaction(rawTxByte); + + if(baseTx.inputs.length == 0 || baseTx.outputs.length == 0) + throw new IllegalArgumentException("Unable to decode given transaction"); + + byte[] txHash = BTCUtils.reverse(CryptoUtil.doubleSha256(rawTxByte)); + String txHashForBuild = current.txID; + byte[] sign = null; + + for (int outputIndex = 0; outputIndex < baseTx.outputs.length; outputIndex++) { + Transaction.Output output = baseTx.outputs[outputIndex]; + + // find outputs + if (Arrays.equals(outputScriptWeAreAbleToSpend, output.script.bytes)) { + unspentOutputs.add(new UnspentOutputInfo(txHash, output.script, output.value, outputIndex, -1, txHashForBuild, sign)); + } + } + + } + + return unspentOutputs; + } + + public static byte[] fromHex(String s) { + if (s != null) { + try { + StringBuilder sb = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if (!Character.isWhitespace(ch)) { + sb.append(ch); + } + } + s = sb.toString(); + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + int hi = (Character.digit(s.charAt(i), 16) << 4); + int low = Character.digit(s.charAt(i + 1), 16); + if (hi >= 256 || low < 0 || low >= 16) { + return null; + } + data[i / 2] = (byte) (hi | low); + } + return data; + } catch (Exception ignored) { + } + } + return null; + } + + public static byte[] reverse(byte[] bytes) { + byte[] result = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + result[i] = bytes[bytes.length - i - 1]; + } + return result; + } + + public static byte[] reverseInPlace(byte[] bytes) { + int len = bytes.length / 2; + for (int i = 0; i < len; i++) { + byte t = bytes[i]; + bytes[i] = bytes[bytes.length - i - 1]; + bytes[bytes.length - i - 1] = t; + } + return bytes; + } + + public static int findSpendableOutput(Transaction tx, String forAddress, long minAmount) throws BitcoinException { + byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(forAddress).bytes; + int indexOfOutputToSpend = -1; + for (int indexOfOutput = 0; indexOfOutput < tx.outputs.length; indexOfOutput++) { + Transaction.Output output = tx.outputs[indexOfOutput]; + if (Arrays.equals(outputScriptWeAreAbleToSpend, output.script.bytes)) { + indexOfOutputToSpend = indexOfOutput; + break;//only one input is supported for now + } + } + if (indexOfOutputToSpend == -1) { + throw new BitcoinException(BitcoinException.ERR_NO_SPENDABLE_OUTPUTS_FOR_THE_ADDRESS, "No spendable standard outputs for " + forAddress + " have found", forAddress); + } + final long spendableOutputValue = tx.outputs[indexOfOutputToSpend].value; + if (spendableOutputValue < minAmount) { + throw new BitcoinException(BitcoinException.ERR_INSUFFICIENT_FUNDS, "Unspent amount is too small: " + spendableOutputValue, spendableOutputValue); + } + return indexOfOutputToSpend; + } + + public static void verify(Transaction.Script[] scripts, Transaction spendTx) throws Transaction.Script.ScriptInvalidException { + for (int i = 0; i < scripts.length; i++) { + Stack stack = new Stack<>(); + spendTx.inputs[i].script.run(stack);//load signature+public key + scripts[i].run(i, spendTx, stack); //verify that this transaction able to spend that output + if (Transaction.Script.verifyFails(stack)) { + throw new Transaction.Script.ScriptInvalidException("Signature is invalid"); + } + } + } + + public static class FeeChangeAndSelectedOutputs { + public final long amountForRecipient, change, fee; + public final ArrayList outputsToSpend; + + public FeeChangeAndSelectedOutputs(long fee, long change, long amountForRecipient, ArrayList outputsToSpend) { + this.fee = fee; + this.change = change; + this.amountForRecipient = amountForRecipient; + this.outputsToSpend = outputsToSpend; + } + } + + public static FeeChangeAndSelectedOutputs calcFeeChangeAndSelectOutputsToSpend(List unspentOutputs, long amountToSend, long extraFee, final boolean isPublicKeyCompressed) throws BitcoinException { + long fee = 0;//calculated below + long change = 0; + long valueOfUnspentOutputs; + ArrayList outputsToSpend = new ArrayList<>(); + if (amountToSend <= 0) { + //transfer all funds from these addresses to outputAddress + change = 0; + valueOfUnspentOutputs = 0; + for (UnspentOutputInfo outputInfo : unspentOutputs) { + outputsToSpend.add(outputInfo); + valueOfUnspentOutputs += outputInfo.value; + } + final int txLen = BTCUtils.getMaximumTxSize(unspentOutputs, 1, isPublicKeyCompressed); + fee = BTCUtils.calcMinimumFee(txLen, unspentOutputs, valueOfUnspentOutputs - MIN_FEE_PER_KB * (1 + txLen / 1000)); + amountToSend = valueOfUnspentOutputs - fee - extraFee; + } else { + valueOfUnspentOutputs = 0; + for (UnspentOutputInfo outputInfo : unspentOutputs) { + outputsToSpend.add(outputInfo); + valueOfUnspentOutputs += outputInfo.value; + long updatedFee = MIN_FEE_PER_KB; + for (int i = 0; i < 3; i++) { + fee = updatedFee; + change = valueOfUnspentOutputs - fee - extraFee - amountToSend; + final int txLen = BTCUtils.getMaximumTxSize(unspentOutputs, change > 0 ? 2 : 1, isPublicKeyCompressed); + updatedFee = BTCUtils.calcMinimumFee(txLen, unspentOutputs, change > 0 ? Math.min(amountToSend, change) : amountToSend); + if (updatedFee == fee) { + break; + } + } + fee = updatedFee; + if (valueOfUnspentOutputs >= amountToSend + fee + extraFee) { + break; + } + } + + } + if (amountToSend > valueOfUnspentOutputs - fee) { + throw new BitcoinException(BitcoinException.ERR_INSUFFICIENT_FUNDS, "Not enough funds", valueOfUnspentOutputs - fee); + } + if (outputsToSpend.isEmpty()) { + throw new BitcoinException(BitcoinException.ERR_NO_INPUT, "No outputs to spend"); + } + if (fee + extraFee > MAX_ALLOWED_FEE) { + throw new BitcoinException(BitcoinException.ERR_FEE_IS_TOO_BIG, "Fee is too big", fee); + } + if (fee < 0 || extraFee < 0) { + throw new BitcoinException(BitcoinException.ERR_FEE_IS_LESS_THEN_ZERO, "Incorrect fee", fee); + } + if (change < 0) { + throw new BitcoinException(BitcoinException.ERR_CHANGE_IS_LESS_THEN_ZERO, "Incorrect change", change); + } + if (amountToSend < 0) { + throw new BitcoinException(BitcoinException.ERR_AMOUNT_TO_SEND_IS_LESS_THEN_ZERO, "Incorrect amount to send", amountToSend); + } + return new FeeChangeAndSelectedOutputs(fee + extraFee, change, amountToSend, outputsToSpend); + + } +} diff --git a/app/src/main/java/com/tangem/wallet/Base58.java b/app/src/main/java/com/tangem/wallet/Base58.java index 0948e420fc..1bb1bef3df 100644 --- a/app/src/main/java/com/tangem/wallet/Base58.java +++ b/app/src/main/java/com/tangem/wallet/Base58.java @@ -1,115 +1,115 @@ -package com.tangem.wallet; - -import java.math.BigInteger; - -/** - * Created by Ilia on 15.02.2018. - */ - -public class Base58 { - private static final char[] BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); - - private static final int BASE58_CHUNK_DIGITS = 10;//how many base 58 digits fits in long - private static final BigInteger BASE58_CHUNK_MOD = BigInteger.valueOf(0x5fa8624c7fba400L); //58^BASE58_CHUNK_DIGITS - private static final byte[] BASE58_VALUES = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, - -1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, - -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, - 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; - - public static byte[] decodeBase58(String input) { - if (input == null) { - return null; - } - input = input.trim(); - if (input.length() == 0) { - return new byte[0]; - } - BigInteger resultNum = BigInteger.ZERO; - int nLeadingZeros = 0; - while (nLeadingZeros < input.length() && input.charAt(nLeadingZeros) == BASE58[0]) { - nLeadingZeros++; - } - long acc = 0; - int nDigits = 0; - int p = nLeadingZeros; - while (p < input.length()) { - int v = BASE58_VALUES[input.charAt(p) & 0xff]; - if (v >= 0) { - acc *= 58; - acc += v; - nDigits++; - if (nDigits == BASE58_CHUNK_DIGITS) { - resultNum = resultNum.multiply(BASE58_CHUNK_MOD).add(BigInteger.valueOf(acc)); - acc = 0; - nDigits = 0; - } - p++; - } else { - break; - } - } - if (nDigits > 0) { - long mul = 58; - while (--nDigits > 0) { - mul *= 58; - } - resultNum = resultNum.multiply(BigInteger.valueOf(mul)).add(BigInteger.valueOf(acc)); - } - final int BASE58_SPACE = -2; - while (p < input.length() && BASE58_VALUES[input.charAt(p) & 0xff] == BASE58_SPACE) { - p++; - } - if (p < input.length()) { - return null; - } - byte[] plainNumber = resultNum.toByteArray(); - int plainNumbersOffs = plainNumber[0] == 0 ? 1 : 0; - byte[] result = new byte[nLeadingZeros + plainNumber.length - plainNumbersOffs]; - System.arraycopy(plainNumber, plainNumbersOffs, result, nLeadingZeros, plainNumber.length - plainNumbersOffs); - return result; - } - - public static String encodeBase58(byte[] input) { - if (input == null) { - return null; - } - StringBuilder str = new StringBuilder((input.length * 350) / 256 + 1); - BigInteger bn = new BigInteger(1, input); - long rem; - while (true) { - BigInteger[] divideAndRemainder = bn.divideAndRemainder(BASE58_CHUNK_MOD); - bn = divideAndRemainder[0]; - rem = divideAndRemainder[1].longValue(); - if (bn.compareTo(BigInteger.ZERO) == 0) { - break; - } - for (int i = 0; i < BASE58_CHUNK_DIGITS; i++) { - str.append(BASE58[(int) (rem % 58)]); - rem /= 58; - } - } - while (rem != 0) { - str.append(BASE58[(int) (rem % 58)]); - rem /= 58; - } - str.reverse(); - int nLeadingZeros = 0; - while (nLeadingZeros < input.length && input[nLeadingZeros] == 0) { - str.insert(0, BASE58[0]); - nLeadingZeros++; - } - return str.toString(); - } +package com.tangem.wallet; + +import java.math.BigInteger; + +/** + * Created by Ilia on 15.02.2018. + */ + +public class Base58 { + private static final char[] BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); + + private static final int BASE58_CHUNK_DIGITS = 10;//how many base 58 digits fits in long + private static final BigInteger BASE58_CHUNK_MOD = BigInteger.valueOf(0x5fa8624c7fba400L); //58^BASE58_CHUNK_DIGITS + private static final byte[] BASE58_VALUES = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -2, -2, -2, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, + -1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1, + -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + public static byte[] decodeBase58(String input) { + if (input == null) { + return null; + } + input = input.trim(); + if (input.length() == 0) { + return new byte[0]; + } + BigInteger resultNum = BigInteger.ZERO; + int nLeadingZeros = 0; + while (nLeadingZeros < input.length() && input.charAt(nLeadingZeros) == BASE58[0]) { + nLeadingZeros++; + } + long acc = 0; + int nDigits = 0; + int p = nLeadingZeros; + while (p < input.length()) { + int v = BASE58_VALUES[input.charAt(p) & 0xff]; + if (v >= 0) { + acc *= 58; + acc += v; + nDigits++; + if (nDigits == BASE58_CHUNK_DIGITS) { + resultNum = resultNum.multiply(BASE58_CHUNK_MOD).add(BigInteger.valueOf(acc)); + acc = 0; + nDigits = 0; + } + p++; + } else { + break; + } + } + if (nDigits > 0) { + long mul = 58; + while (--nDigits > 0) { + mul *= 58; + } + resultNum = resultNum.multiply(BigInteger.valueOf(mul)).add(BigInteger.valueOf(acc)); + } + final int BASE58_SPACE = -2; + while (p < input.length() && BASE58_VALUES[input.charAt(p) & 0xff] == BASE58_SPACE) { + p++; + } + if (p < input.length()) { + return null; + } + byte[] plainNumber = resultNum.toByteArray(); + int plainNumbersOffs = plainNumber[0] == 0 ? 1 : 0; + byte[] result = new byte[nLeadingZeros + plainNumber.length - plainNumbersOffs]; + System.arraycopy(plainNumber, plainNumbersOffs, result, nLeadingZeros, plainNumber.length - plainNumbersOffs); + return result; + } + + public static String encodeBase58(byte[] input) { + if (input == null) { + return null; + } + StringBuilder str = new StringBuilder((input.length * 350) / 256 + 1); + BigInteger bn = new BigInteger(1, input); + long rem; + while (true) { + BigInteger[] divideAndRemainder = bn.divideAndRemainder(BASE58_CHUNK_MOD); + bn = divideAndRemainder[0]; + rem = divideAndRemainder[1].longValue(); + if (bn.compareTo(BigInteger.ZERO) == 0) { + break; + } + for (int i = 0; i < BASE58_CHUNK_DIGITS; i++) { + str.append(BASE58[(int) (rem % 58)]); + rem /= 58; + } + } + while (rem != 0) { + str.append(BASE58[(int) (rem % 58)]); + rem /= 58; + } + str.reverse(); + int nLeadingZeros = 0; + while (nLeadingZeros < input.length && input[nLeadingZeros] == 0) { + str.insert(0, BASE58[0]); + nLeadingZeros++; + } + return str.toString(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/BitcoinException.java b/app/src/main/java/com/tangem/wallet/BitcoinException.java index 4051775e60..0d3f52c547 100644 --- a/app/src/main/java/com/tangem/wallet/BitcoinException.java +++ b/app/src/main/java/com/tangem/wallet/BitcoinException.java @@ -1,35 +1,35 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 29.09.2017. - */ - -@SuppressWarnings("WeakerAccess") -public final class BitcoinException extends Exception { - public static final int ERR_NO_SPENDABLE_OUTPUTS_FOR_THE_ADDRESS = 0; - public static final int ERR_INSUFFICIENT_FUNDS = 1; - public static final int ERR_WRONG_TYPE = 2; - public static final int ERR_BAD_FORMAT = 3; - public static final int ERR_INCORRECT_PASSWORD = 4; - public static final int ERR_MEANINGLESS_OPERATION = 5; - public static final int ERR_NO_INPUT = 6; - public static final int ERR_FEE_IS_TOO_BIG = 7; - public static final int ERR_FEE_IS_LESS_THEN_ZERO = 8; - public static final int ERR_CHANGE_IS_LESS_THEN_ZERO = 9; - public static final int ERR_AMOUNT_TO_SEND_IS_LESS_THEN_ZERO = 10; - public static final int ERR_UNSUPPORTED = 11; - - public final int errorCode; - @SuppressWarnings({"WeakerAccess", "unused"}) - public final Object extraInformation; - - public BitcoinException(int errorCode, String detailMessage, Object extraInformation) { - super(detailMessage); - this.errorCode = errorCode; - this.extraInformation = extraInformation; - } - - public BitcoinException(int errorCode, String detailMessage) { - this(errorCode, detailMessage, null); - } -} +package com.tangem.wallet; + +/** + * Created by Ilia on 29.09.2017. + */ + +@SuppressWarnings("WeakerAccess") +public final class BitcoinException extends Exception { + public static final int ERR_NO_SPENDABLE_OUTPUTS_FOR_THE_ADDRESS = 0; + public static final int ERR_INSUFFICIENT_FUNDS = 1; + public static final int ERR_WRONG_TYPE = 2; + public static final int ERR_BAD_FORMAT = 3; + public static final int ERR_INCORRECT_PASSWORD = 4; + public static final int ERR_MEANINGLESS_OPERATION = 5; + public static final int ERR_NO_INPUT = 6; + public static final int ERR_FEE_IS_TOO_BIG = 7; + public static final int ERR_FEE_IS_LESS_THEN_ZERO = 8; + public static final int ERR_CHANGE_IS_LESS_THEN_ZERO = 9; + public static final int ERR_AMOUNT_TO_SEND_IS_LESS_THEN_ZERO = 10; + public static final int ERR_UNSUPPORTED = 11; + + public final int errorCode; + @SuppressWarnings({"WeakerAccess", "unused"}) + public final Object extraInformation; + + public BitcoinException(int errorCode, String detailMessage, Object extraInformation) { + super(detailMessage); + this.errorCode = errorCode; + this.extraInformation = extraInformation; + } + + public BitcoinException(int errorCode, String detailMessage) { + this(errorCode, detailMessage, null); + } +} diff --git a/app/src/main/java/com/tangem/wallet/BitcoinInputStream.java b/app/src/main/java/com/tangem/wallet/BitcoinInputStream.java index fa0ed57c0d..4c66f81044 100644 --- a/app/src/main/java/com/tangem/wallet/BitcoinInputStream.java +++ b/app/src/main/java/com/tangem/wallet/BitcoinInputStream.java @@ -1,69 +1,69 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 29.09.2017. - */ - -import java.io.ByteArrayInputStream; -import java.io.EOFException; -import java.io.IOException; - -@SuppressWarnings("WeakerAccess") -public class BitcoinInputStream extends ByteArrayInputStream { - public BitcoinInputStream(byte[] buf) { - super(buf); - } - - @SuppressWarnings("unused") - public BitcoinInputStream(byte[] buf, int offset, int length) { - super(buf, offset, length); - } - - public int readInt16() throws EOFException { - return (readByte() & 0xff) | ((readByte() & 0xff) << 8); - } - - public int readInt32() throws EOFException { - return (readByte() & 0xff) | ((readByte() & 0xff) << 8) | ((readByte() & 0xff) << 16) | ((readByte() & 0xff) << 24); - } - - public long readInt64() throws EOFException { - return (readInt32() & 0xFFFFFFFFL )| ((readInt32() & 0xFFFFFFFFL) << 32); - } - - public int readByte() throws EOFException { - int readedByte = super.read(); - if (readedByte == -1) { - throw new EOFException(); - } - return readedByte; - } - - public long readVarInt() throws EOFException { - int readedByte = readByte(); - if (readedByte < 0xfd) { - return readedByte; - } else if (readedByte == 0xfd) { - return readInt16(); - } else if (readedByte == 0xfe) { - return readInt32(); - } else { - return readInt64(); - } - } - - public byte[] readChars(final int count) throws IOException { - byte[] buf = new byte[count]; - int off = 0; - while (off != count) { - int bytesReadCurr = read(buf, off, count - off); - if (bytesReadCurr == -1) { - throw new EOFException(); - } else { - off += bytesReadCurr; - } - } - return buf; - } - -} +package com.tangem.wallet; + +/** + * Created by Ilia on 29.09.2017. + */ + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.IOException; + +@SuppressWarnings("WeakerAccess") +public class BitcoinInputStream extends ByteArrayInputStream { + public BitcoinInputStream(byte[] buf) { + super(buf); + } + + @SuppressWarnings("unused") + public BitcoinInputStream(byte[] buf, int offset, int length) { + super(buf, offset, length); + } + + public int readInt16() throws EOFException { + return (readByte() & 0xff) | ((readByte() & 0xff) << 8); + } + + public int readInt32() throws EOFException { + return (readByte() & 0xff) | ((readByte() & 0xff) << 8) | ((readByte() & 0xff) << 16) | ((readByte() & 0xff) << 24); + } + + public long readInt64() throws EOFException { + return (readInt32() & 0xFFFFFFFFL )| ((readInt32() & 0xFFFFFFFFL) << 32); + } + + public int readByte() throws EOFException { + int readedByte = super.read(); + if (readedByte == -1) { + throw new EOFException(); + } + return readedByte; + } + + public long readVarInt() throws EOFException { + int readedByte = readByte(); + if (readedByte < 0xfd) { + return readedByte; + } else if (readedByte == 0xfd) { + return readInt16(); + } else if (readedByte == 0xfe) { + return readInt32(); + } else { + return readInt64(); + } + } + + public byte[] readChars(final int count) throws IOException { + byte[] buf = new byte[count]; + int off = 0; + while (off != count) { + int bytesReadCurr = read(buf, off, count - off); + if (bytesReadCurr == -1) { + throw new EOFException(); + } else { + off += bytesReadCurr; + } + } + return buf; + } + +} diff --git a/app/src/main/java/com/tangem/wallet/BitcoinOutputStream.java b/app/src/main/java/com/tangem/wallet/BitcoinOutputStream.java index 7963eee0d5..baa5985b59 100644 --- a/app/src/main/java/com/tangem/wallet/BitcoinOutputStream.java +++ b/app/src/main/java/com/tangem/wallet/BitcoinOutputStream.java @@ -1,42 +1,42 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 29.09.2017. - */ -import java.io.ByteArrayOutputStream; - -@SuppressWarnings("WeakerAccess") -public final class BitcoinOutputStream extends ByteArrayOutputStream { - - public void writeInt16(int value) { - write(value & 0xff); - write((value >> 8) & 0xff); - } - - public void writeInt32(int value) { - write(value & 0xff); - write((value >> 8) & 0xff); - write((value >> 16) & 0xff); - write((value >>> 24) & 0xff); - } - - public void writeInt64(long value) { - writeInt32((int) (value & 0xFFFFFFFFL)); - writeInt32((int) ((value >>> 32) & 0xFFFFFFFFL)); - } - - public void writeVarInt(long value) { - if (value < 0xfd) { - write((int) (value & 0xff)); - } else if (value < 0xffff) { - write(0xfd); - writeInt16((int) value); - } else if (value < 0xffffffffL) { - write(0xfe); - writeInt32((int) value); - } else { - write(0xff); - writeInt64(value); - } - } +package com.tangem.wallet; + +/** + * Created by Ilia on 29.09.2017. + */ +import java.io.ByteArrayOutputStream; + +@SuppressWarnings("WeakerAccess") +public final class BitcoinOutputStream extends ByteArrayOutputStream { + + public void writeInt16(int value) { + write(value & 0xff); + write((value >> 8) & 0xff); + } + + public void writeInt32(int value) { + write(value & 0xff); + write((value >> 8) & 0xff); + write((value >> 16) & 0xff); + write((value >>> 24) & 0xff); + } + + public void writeInt64(long value) { + writeInt32((int) (value & 0xFFFFFFFFL)); + writeInt32((int) ((value >>> 32) & 0xFFFFFFFFL)); + } + + public void writeVarInt(long value) { + if (value < 0xfd) { + write((int) (value & 0xff)); + } else if (value < 0xffff) { + write(0xfd); + writeInt16((int) value); + } else if (value < 0xffffffffL) { + write(0xfe); + writeInt32((int) value); + } else { + write(0xff); + writeInt64(value); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/Blockchain.java b/app/src/main/java/com/tangem/wallet/Blockchain.java index bf214a32a8..53960c684c 100644 --- a/app/src/main/java/com/tangem/wallet/Blockchain.java +++ b/app/src/main/java/com/tangem/wallet/Blockchain.java @@ -1,99 +1,99 @@ -package com.tangem.wallet; - -import android.net.Uri; - -import com.google.common.base.Strings; -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.Util; - -import org.bitcoinj.core.Base58; - -import java.nio.ByteBuffer; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -/** - * Created by dvol on 06.08.2017. - */ -public enum Blockchain { - Unknown("", "", 1.0, R.drawable.ic_logo_small, ""), - Bitcoin("BTC", "BTC", 100000000.0, R.drawable.bitcoins, "Bitcoin"), - BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.bitcoins_testnet, "Bitcoin Testnet"), - Ethereum("ETH", "ETH", 1.0, R.drawable.ethereum, "Ethereum"), - EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ethereum_testnet, "Ethereum Testnet"), - Token("ETH\\XTZ", "BAT", 1.0, R.drawable.bat_token, "Ethereum"), - BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.bitcoin_cash, "Bitcoin Cash"), - BitcoinCashTestNet("BCH/test", "BTC", 100000000.0, R.drawable.bitcoin_cash, "Bitcoin Cash Testnet"); - - - Blockchain(String ID, String Currency, double Multiplier, int ImageResource, String officialName) { - mID = ID; - mCurrency = Currency; - mMultiplier = Multiplier; - mImageResource = ImageResource; - mOfficialName = officialName; - } - - private String mID, mOfficialName; - private double mMultiplier; - private String mCurrency; - private int mImageResource; - - public String getID() { - return mID; - } - - public String getOfficialName() { - return mOfficialName; - } - - public double getMultiplier() { - return mMultiplier; - } - - public String getCurrency() { - return mCurrency; - } - - public static Blockchain fromId(String id) { - for (Blockchain blockchain : values()) { - if (blockchain.getID().equals(id)) return blockchain; - } - return null; - } - - public static Blockchain fromCurrency(String currency) { - for (Blockchain blockchain : values()) { - if (blockchain.getCurrency() == currency) return blockchain; - } - return null; - } - - public static String[] getCurrencies() { - String[] result = new String[values().length - 1]; - for (int i = 0; i < result.length - 1; i++) { - result[i] = values()[i + 1].getCurrency(); - } - return result; - } - - public int getImageResource() { - return mImageResource; - } - - public int getImageResource(android.content.Context context, String name) { - if(Strings.isNullOrEmpty(name)) - return getImageResource(); - - name = name.toLowerCase(); - - int resourceId = context.getResources().getIdentifier(name+"_token", "drawable", context.getPackageName()); - - if(resourceId <= 0) - return R.drawable.ethereum; - return resourceId; - } -} +package com.tangem.wallet; + +import android.net.Uri; + +import com.google.common.base.Strings; +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.Util; + +import org.bitcoinj.core.Base58; + +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Created by dvol on 06.08.2017. + */ +public enum Blockchain { + Unknown("", "", 1.0, R.drawable.ic_logo_small, ""), + Bitcoin("BTC", "BTC", 100000000.0, R.drawable.bitcoins, "Bitcoin"), + BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.bitcoins_testnet, "Bitcoin Testnet"), + Ethereum("ETH", "ETH", 1.0, R.drawable.ethereum, "Ethereum"), + EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ethereum_testnet, "Ethereum Testnet"), + Token("ETH\\XTZ", "BAT", 1.0, R.drawable.bat_token, "Ethereum"), + BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.bitcoin_cash, "Bitcoin Cash"), + BitcoinCashTestNet("BCH/test", "BTC", 100000000.0, R.drawable.bitcoin_cash, "Bitcoin Cash Testnet"); + + + Blockchain(String ID, String Currency, double Multiplier, int ImageResource, String officialName) { + mID = ID; + mCurrency = Currency; + mMultiplier = Multiplier; + mImageResource = ImageResource; + mOfficialName = officialName; + } + + private String mID, mOfficialName; + private double mMultiplier; + private String mCurrency; + private int mImageResource; + + public String getID() { + return mID; + } + + public String getOfficialName() { + return mOfficialName; + } + + public double getMultiplier() { + return mMultiplier; + } + + public String getCurrency() { + return mCurrency; + } + + public static Blockchain fromId(String id) { + for (Blockchain blockchain : values()) { + if (blockchain.getID().equals(id)) return blockchain; + } + return null; + } + + public static Blockchain fromCurrency(String currency) { + for (Blockchain blockchain : values()) { + if (blockchain.getCurrency() == currency) return blockchain; + } + return null; + } + + public static String[] getCurrencies() { + String[] result = new String[values().length - 1]; + for (int i = 0; i < result.length - 1; i++) { + result[i] = values()[i + 1].getCurrency(); + } + return result; + } + + public int getImageResource() { + return mImageResource; + } + + public int getImageResource(android.content.Context context, String name) { + if(Strings.isNullOrEmpty(name)) + return getImageResource(); + + name = name.toLowerCase(); + + int resourceId = context.getResources().getIdentifier(name+"_token", "drawable", context.getPackageName()); + + if(resourceId <= 0) + return R.drawable.ethereum; + return resourceId; + } +} diff --git a/app/src/main/java/com/tangem/wallet/BtcCashEngine.java b/app/src/main/java/com/tangem/wallet/BtcCashEngine.java index 076d7b2905..bf8850105a 100644 --- a/app/src/main/java/com/tangem/wallet/BtcCashEngine.java +++ b/app/src/main/java/com/tangem/wallet/BtcCashEngine.java @@ -1,428 +1,428 @@ -package com.tangem.wallet; - -import android.net.Uri; - -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.TLV; -import com.tangem.cardReader.Util; - -import java.io.ByteArrayOutputStream; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import static com.tangem.wallet.FormatUtil.GetDecimalFormat; - -/** - * Created by Ilia on 15.02.2018. - */ - -public class BtcCashEngine extends CoinEngine{ - public String GetNextNode(Tangem_Card mCard) - { - - return "35.157.238.5"; - } - public int GetNextNodePort(Tangem_Card mCard) - { - - return 51001; - } - public String GetNode(Tangem_Card mCard) - { - return "35.157.238.5"; - } - public int GetNodePort(Tangem_Card mCard) - { - return 51001; - } - - public void SwitchNode(Tangem_Card mCard) - { - } - - public boolean InOutPutVisible() - { - return true; - } - - public boolean AwaitingConfirmation(Tangem_Card card) - { - return card.getBalanceUnconfirmed()!=0; - } - - public String GetBalanceWithAlter(Tangem_Card mCard) - { - return GetBalance(mCard); - } - - public boolean IsBalanceAlterNotZero(Tangem_Card card) - { - return true; - } - - public Long GetBalanceLong(Tangem_Card mCard) - { - return mCard.getBalance(); - } - - public boolean IsBalanceNotZero(Tangem_Card card) - { - return card.getBalance() > 0; - } - - public boolean CheckAmount(Tangem_Card card, String amount) throws Exception - { - DecimalFormat decimalFormat = GetDecimalFormat(); - BigDecimal amountValue = (BigDecimal) decimalFormat.parse(amount); - - // Convert Balance to BigDecimal - BigDecimal maxValue = new BigDecimal(GetBalanceValue(card)); - maxValue = maxValue.divide(new BigDecimal(1000)); - - //if (use_mCurrency) { - amountValue = amountValue.divide(new BigDecimal(1000)); - //} - - if (amountValue.compareTo(maxValue) > 0) { - return false; - } - - return true; - } - - public boolean HasBalanceInfo(Tangem_Card card) - { - return card.hasBalanceInfo(); - } - - public String GetBalanceCurrency(Tangem_Card card) - { - return "mBCH"; - } - - public boolean CheckUnspentTransaction(Tangem_Card mCard) - { - return mCard.getUnspentTransactions().size() != 0; - } - - public String GetFeeCurrency() - { - return "mBCH"; - } - - public boolean ValdateAddress(String address, Tangem_Card card){ - if(address == null || address.isEmpty()) - { - return false; - } - - if(address.length() < 25) - { - return false; - } - - if(address.length() > 35) - { - return false; - } - - if(!address.startsWith("1") && !address.startsWith("2") && !address.startsWith("3") && !address.startsWith("n") && !address.startsWith("m")) - { - return false; - } - - byte[] decAddress = Base58.decodeBase58(address); - - if(decAddress == null || decAddress.length == 0) - { - return false; - } - - byte[] rip = new byte[21]; - for(int i =0; i < 21; ++i) - { - rip[i] = decAddress[i]; - } - - byte[] kcv = CryptoUtil.doubleSha256(rip); - - for(int i =0; i < 4; ++i) - { - if(kcv[i] != decAddress[21+i]) - return false; - } - - if(card.getBlockchain()!=Blockchain.BitcoinCashTestNet && card.getBlockchain()!=Blockchain.BitcoinCash) - { - return false; - } - - if(card.getBlockchain()==Blockchain.BitcoinCashTestNet && (address.startsWith("1") || address.startsWith("3"))) - { - return false; - } - - return true; - } - - - public int GetTokenDecimals(Tangem_Card card) - { - return 0; - } - - public String GetContractAddress(Tangem_Card card) - { - return ""; - } - - public boolean IsNeedCheckNode() - { - return true; - } - - public Uri getShareWalletURIExplorer(Tangem_Card mCard) - { - return Uri.parse((mCard.getBlockchain() == Blockchain.BitcoinCash ? "https://bitcoincash.blockexplorer.com/address/" : "https://testnet.blockexplorer.com/address/") + mCard.getWallet()); - } - public Uri getShareWalletURI(Tangem_Card mCard) - { - return Uri.parse("bitcoincash:" + mCard.getWallet()); - } - public boolean CheckAmountValie(Tangem_Card mCard, String amountValue, String feeValue, Long minFeeInInternalUnits) - { - Long fee = null; - Long amount = null; - try { - amount = mCard.InternalUnitsFromString(amountValue); - fee = mCard.InternalUnitsFromString(feeValue); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - - if(fee == null || amount == null) - return false; - - if(fee == 0 || amount ==0) - return false; - - if(fee > amount) - return false; - - if(fee < minFeeInInternalUnits) - return false; - - return true; - } - - public String EvaluteFeeEquivalent(Tangem_Card mCard, String fee) - { - return GetAmountEqualentDescriptor(mCard, fee); - } - - @Override - public String GetBalanceEquivalent(Tangem_Card mCard) { - Double balance = Double.NaN; - try{ - Long val = mCard.getBalance(); - balance = mCard.AmountFromInternalUnits(val); - } - catch(Exception ex) - { - mCard.setRate(0); - } - - return mCard.getAmountEquivalentDescription(balance); - } - - public String GetBalance(Tangem_Card mCard) - { - if (mCard.hasBalanceInfo()) { - Double balance = mCard.AmountFromInternalUnits(mCard.getBalance()); - return mCard.getAmountDescription(balance); - } else { - return "-- -- -- " + mCard.getBlockchain().getCurrency(); - } - } - - public String GetBalanceValue(Tangem_Card mCard) - { - if (mCard.hasBalanceInfo()) { - Double balance = mCard.getBalance() / (mCard.getBlockchain().getMultiplier() / 1000.0); - - String output = FormatUtil.DoubleToString(balance); - //String pattern = "#0.000"; // If you like 4 zeros - //DecimalFormat myFormatter = new DecimalFormat(pattern); - //String output = myFormatter.format(balance); - return output; - - //return Double.toString(balance); - } - else - { - return "0"; - } - } - - public String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException { - - byte netSelectionByte; - switch (mCard.getBlockchain()) { - case BitcoinCash: - netSelectionByte = (byte) 0x00; //0 - MainNet 0x6f - TestNet - break; - default : - netSelectionByte = (byte) 0x6f; //0 - MainNet 0x6f - TestNet - break; - } - - byte hash1[] = Util.calculateSHA256(pkUncompressed); - byte hash2[] = Util.calculateRIPEMD160(hash1); - - ByteBuffer BB = ByteBuffer.allocate(hash2.length + 1); - - BB.put(netSelectionByte); - BB.put(hash2); - - byte hash3[] = Util.calculateSHA256(BB.array()); - byte hash4[] = Util.calculateSHA256(hash3); - - BB = ByteBuffer.allocate(hash2.length + 5); - BB.put(netSelectionByte); //BB.put((byte) 0x6f); - BB.put(hash2); - BB.put(hash4[0]); - BB.put(hash4[1]); - BB.put(hash4[2]); - BB.put(hash4[3]); - - return org.bitcoinj.core.Base58.encode(BB.array()); - - } - - @Override - public String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception { - byte[] reversed=new byte[bytes.length]; - for(int i=0; i 0) { - return String.format("≈ USD %.2f", amount * rate); - } else { - return "≈ USD  ---"; - } - } - - public String GetAmountEqualentDescriptor(Tangem_Card mCard, String value) - { - return getAmountEquivalentDescriptionBTC(Double.parseDouble(value)/1000.0, mCard.getRate()); - } - - public byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception { - - String myAddress = mCard.getWallet(); - byte[] pbKey = mCard.getWalletPublicKeyRar(); //ALWAYS USING COMPRESS KEY - String outputAddress = toValue; - String changeAddress = myAddress; - - // Build script for our address - List rawTxList = mCard.getUnspentTransactions(); - byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; - - // Collect unspent - ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); - - long fullAmount = 0; - for (int i = 0; i < unspentOutputs.size(); ++i) { - fullAmount += unspentOutputs.get(i).value; - } - - - long fees = FormatUtil.ConvertStringToLong(feeValue); - long amount = FormatUtil.ConvertStringToLong(amountValue); - amount = amount - fees; - - long change = fullAmount - fees - amount; - - if (amount + fees > fullAmount) { - throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); - } - - byte[][] dataForSign = new byte[unspentOutputs.size()][]; - - for (int i = 0; i < unspentOutputs.size(); ++i) { - byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, changeAddress, unspentOutputs, i, amount, change); - - byte[] hashData = Util.calculateSHA256(newTX); - byte[] doubleHashData = Util.calculateSHA256(hashData); - - unspentOutputs.get(i).bodyDoubleHash = doubleHashData; - unspentOutputs.get(i).bodyHash = hashData; - - if(mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw || mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw_Validated_By_Issuer) - { - dataForSign[i] = newTX; - } - else - { - dataForSign[i] = doubleHashData; - } - - } - - byte[] signFromCard = null; - if(mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw || mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw_Validated_By_Issuer) - { - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!"); - for (int i = 0; i < dataForSign.length; i++) { - if (i != 0 && dataForSign[0].length != dataForSign[i].length) - throw new Exception("Hashes length must be identical!"); - bs.write(dataForSign[i]); - } - signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), bs.toByteArray()).getTLV(TLV.Tag.TAG_Signature).Value; - } - else { - signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Hash_Validated_By_Issuer, null, mCard.getIssuer()).getTLV(TLV.Tag.TAG_Signature).Value; - // TODO slice signFromCard to hashes.length parts - } - - LastSignStorage.setLastSignDate(mCard.getWallet(), new Date()); - - - for (int i = 0; i < unspentOutputs.size(); ++i) { - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); - s = CryptoUtil.toCanonicalised(s); - byte[] encodingSign = DerEncodingUtil.packSignDerBitcoinCash(r, s, pbKey); - - unspentOutputs.get(i).scriptForBuild = encodingSign; - } - - byte[] realTX = BTCUtils.buildTXForSend(outputAddress, changeAddress, unspentOutputs, amount, change); - return realTX; - } -} +package com.tangem.wallet; + +import android.net.Uri; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.TLV; +import com.tangem.cardReader.Util; + +import java.io.ByteArrayOutputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import static com.tangem.wallet.FormatUtil.GetDecimalFormat; + +/** + * Created by Ilia on 15.02.2018. + */ + +public class BtcCashEngine extends CoinEngine{ + public String GetNextNode(Tangem_Card mCard) + { + + return "35.157.238.5"; + } + public int GetNextNodePort(Tangem_Card mCard) + { + + return 51001; + } + public String GetNode(Tangem_Card mCard) + { + return "35.157.238.5"; + } + public int GetNodePort(Tangem_Card mCard) + { + return 51001; + } + + public void SwitchNode(Tangem_Card mCard) + { + } + + public boolean InOutPutVisible() + { + return true; + } + + public boolean AwaitingConfirmation(Tangem_Card card) + { + return card.getBalanceUnconfirmed()!=0; + } + + public String GetBalanceWithAlter(Tangem_Card mCard) + { + return GetBalance(mCard); + } + + public boolean IsBalanceAlterNotZero(Tangem_Card card) + { + return true; + } + + public Long GetBalanceLong(Tangem_Card mCard) + { + return mCard.getBalance(); + } + + public boolean IsBalanceNotZero(Tangem_Card card) + { + return card.getBalance() > 0; + } + + public boolean CheckAmount(Tangem_Card card, String amount) throws Exception + { + DecimalFormat decimalFormat = GetDecimalFormat(); + BigDecimal amountValue = (BigDecimal) decimalFormat.parse(amount); + + // Convert Balance to BigDecimal + BigDecimal maxValue = new BigDecimal(GetBalanceValue(card)); + maxValue = maxValue.divide(new BigDecimal(1000)); + + //if (use_mCurrency) { + amountValue = amountValue.divide(new BigDecimal(1000)); + //} + + if (amountValue.compareTo(maxValue) > 0) { + return false; + } + + return true; + } + + public boolean HasBalanceInfo(Tangem_Card card) + { + return card.hasBalanceInfo(); + } + + public String GetBalanceCurrency(Tangem_Card card) + { + return "mBCH"; + } + + public boolean CheckUnspentTransaction(Tangem_Card mCard) + { + return mCard.getUnspentTransactions().size() != 0; + } + + public String GetFeeCurrency() + { + return "mBCH"; + } + + public boolean ValdateAddress(String address, Tangem_Card card){ + if(address == null || address.isEmpty()) + { + return false; + } + + if(address.length() < 25) + { + return false; + } + + if(address.length() > 35) + { + return false; + } + + if(!address.startsWith("1") && !address.startsWith("2") && !address.startsWith("3") && !address.startsWith("n") && !address.startsWith("m")) + { + return false; + } + + byte[] decAddress = Base58.decodeBase58(address); + + if(decAddress == null || decAddress.length == 0) + { + return false; + } + + byte[] rip = new byte[21]; + for(int i =0; i < 21; ++i) + { + rip[i] = decAddress[i]; + } + + byte[] kcv = CryptoUtil.doubleSha256(rip); + + for(int i =0; i < 4; ++i) + { + if(kcv[i] != decAddress[21+i]) + return false; + } + + if(card.getBlockchain()!=Blockchain.BitcoinCashTestNet && card.getBlockchain()!=Blockchain.BitcoinCash) + { + return false; + } + + if(card.getBlockchain()==Blockchain.BitcoinCashTestNet && (address.startsWith("1") || address.startsWith("3"))) + { + return false; + } + + return true; + } + + + public int GetTokenDecimals(Tangem_Card card) + { + return 0; + } + + public String GetContractAddress(Tangem_Card card) + { + return ""; + } + + public boolean IsNeedCheckNode() + { + return true; + } + + public Uri getShareWalletURIExplorer(Tangem_Card mCard) + { + return Uri.parse((mCard.getBlockchain() == Blockchain.BitcoinCash ? "https://bitcoincash.blockexplorer.com/address/" : "https://testnet.blockexplorer.com/address/") + mCard.getWallet()); + } + public Uri getShareWalletURI(Tangem_Card mCard) + { + return Uri.parse("bitcoincash:" + mCard.getWallet()); + } + public boolean CheckAmountValie(Tangem_Card mCard, String amountValue, String feeValue, Long minFeeInInternalUnits) + { + Long fee = null; + Long amount = null; + try { + amount = mCard.InternalUnitsFromString(amountValue); + fee = mCard.InternalUnitsFromString(feeValue); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + if(fee == null || amount == null) + return false; + + if(fee == 0 || amount ==0) + return false; + + if(fee > amount) + return false; + + if(fee < minFeeInInternalUnits) + return false; + + return true; + } + + public String EvaluteFeeEquivalent(Tangem_Card mCard, String fee) + { + return GetAmountEqualentDescriptor(mCard, fee); + } + + @Override + public String GetBalanceEquivalent(Tangem_Card mCard) { + Double balance = Double.NaN; + try{ + Long val = mCard.getBalance(); + balance = mCard.AmountFromInternalUnits(val); + } + catch(Exception ex) + { + mCard.setRate(0); + } + + return mCard.getAmountEquivalentDescription(balance); + } + + public String GetBalance(Tangem_Card mCard) + { + if (mCard.hasBalanceInfo()) { + Double balance = mCard.AmountFromInternalUnits(mCard.getBalance()); + return mCard.getAmountDescription(balance); + } else { + return "-- -- -- " + mCard.getBlockchain().getCurrency(); + } + } + + public String GetBalanceValue(Tangem_Card mCard) + { + if (mCard.hasBalanceInfo()) { + Double balance = mCard.getBalance() / (mCard.getBlockchain().getMultiplier() / 1000.0); + + String output = FormatUtil.DoubleToString(balance); + //String pattern = "#0.000"; // If you like 4 zeros + //DecimalFormat myFormatter = new DecimalFormat(pattern); + //String output = myFormatter.format(balance); + return output; + + //return Double.toString(balance); + } + else + { + return "0"; + } + } + + public String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException { + + byte netSelectionByte; + switch (mCard.getBlockchain()) { + case BitcoinCash: + netSelectionByte = (byte) 0x00; //0 - MainNet 0x6f - TestNet + break; + default : + netSelectionByte = (byte) 0x6f; //0 - MainNet 0x6f - TestNet + break; + } + + byte hash1[] = Util.calculateSHA256(pkUncompressed); + byte hash2[] = Util.calculateRIPEMD160(hash1); + + ByteBuffer BB = ByteBuffer.allocate(hash2.length + 1); + + BB.put(netSelectionByte); + BB.put(hash2); + + byte hash3[] = Util.calculateSHA256(BB.array()); + byte hash4[] = Util.calculateSHA256(hash3); + + BB = ByteBuffer.allocate(hash2.length + 5); + BB.put(netSelectionByte); //BB.put((byte) 0x6f); + BB.put(hash2); + BB.put(hash4[0]); + BB.put(hash4[1]); + BB.put(hash4[2]); + BB.put(hash4[3]); + + return org.bitcoinj.core.Base58.encode(BB.array()); + + } + + @Override + public String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception { + byte[] reversed=new byte[bytes.length]; + for(int i=0; i 0) { + return String.format("≈ USD %.2f", amount * rate); + } else { + return "≈ USD  ---"; + } + } + + public String GetAmountEqualentDescriptor(Tangem_Card mCard, String value) + { + return getAmountEquivalentDescriptionBTC(Double.parseDouble(value)/1000.0, mCard.getRate()); + } + + public byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception { + + String myAddress = mCard.getWallet(); + byte[] pbKey = mCard.getWalletPublicKeyRar(); //ALWAYS USING COMPRESS KEY + String outputAddress = toValue; + String changeAddress = myAddress; + + // Build script for our address + List rawTxList = mCard.getUnspentTransactions(); + byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; + + // Collect unspent + ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + + long fullAmount = 0; + for (int i = 0; i < unspentOutputs.size(); ++i) { + fullAmount += unspentOutputs.get(i).value; + } + + + long fees = FormatUtil.ConvertStringToLong(feeValue); + long amount = FormatUtil.ConvertStringToLong(amountValue); + amount = amount - fees; + + long change = fullAmount - fees - amount; + + if (amount + fees > fullAmount) { + throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); + } + + byte[][] dataForSign = new byte[unspentOutputs.size()][]; + + for (int i = 0; i < unspentOutputs.size(); ++i) { + byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, changeAddress, unspentOutputs, i, amount, change); + + byte[] hashData = Util.calculateSHA256(newTX); + byte[] doubleHashData = Util.calculateSHA256(hashData); + + unspentOutputs.get(i).bodyDoubleHash = doubleHashData; + unspentOutputs.get(i).bodyHash = hashData; + + if(mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw || mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw_Validated_By_Issuer) + { + dataForSign[i] = newTX; + } + else + { + dataForSign[i] = doubleHashData; + } + + } + + byte[] signFromCard = null; + if(mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw || mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw_Validated_By_Issuer) + { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!"); + for (int i = 0; i < dataForSign.length; i++) { + if (i != 0 && dataForSign[0].length != dataForSign[i].length) + throw new Exception("Hashes length must be identical!"); + bs.write(dataForSign[i]); + } + signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), bs.toByteArray()).getTLV(TLV.Tag.TAG_Signature).Value; + } + else { + signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Hash_Validated_By_Issuer, null, mCard.getIssuer()).getTLV(TLV.Tag.TAG_Signature).Value; + // TODO slice signFromCard to hashes.length parts + } + + LastSignStorage.setLastSignDate(mCard.getWallet(), new Date()); + + + for (int i = 0; i < unspentOutputs.size(); ++i) { + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); + s = CryptoUtil.toCanonicalised(s); + byte[] encodingSign = DerEncodingUtil.packSignDerBitcoinCash(r, s, pbKey); + + unspentOutputs.get(i).scriptForBuild = encodingSign; + } + + byte[] realTX = BTCUtils.buildTXForSend(outputAddress, changeAddress, unspentOutputs, amount, change); + return realTX; + } +} diff --git a/app/src/main/java/com/tangem/wallet/BtcEngine.java b/app/src/main/java/com/tangem/wallet/BtcEngine.java index 963ff746d9..1181f2e6db 100644 --- a/app/src/main/java/com/tangem/wallet/BtcEngine.java +++ b/app/src/main/java/com/tangem/wallet/BtcEngine.java @@ -1,564 +1,564 @@ -package com.tangem.wallet; - -import android.net.Uri; - -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.TLV; -import com.tangem.cardReader.Util; - -import java.io.ByteArrayOutputStream; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Random; - -import static com.tangem.wallet.FormatUtil.GetDecimalFormat; - -/** - * Created by Ilia on 15.02.2018. - */ - -public class BtcEngine extends CoinEngine{ - public String GetNextNode(Tangem_Card mCard) - { - return getNextServiceHost(mCard); - } - public int GetNextNodePort(Tangem_Card mCard) - { - return getNextServicePort(mCard); - } - public String GetNode(Tangem_Card mCard) - { - return getServiceHost(mCard); - } - public int GetNodePort(Tangem_Card mCard) - { - return getServicePort(mCard); - } - - public void SwitchNode(Tangem_Card mCard) - { - SelectNextBitconServiceIndex(); - } - - public static String[] GetBitcoinServiceHosts() { - return new String[]{"vps.hsmiths.com", "tardis.bauerj.eu" /*"arihancckjge66iv.onion"*/, "electrumx.bot.nu","electrumx.hopto.org"/* "btc.asis.io"*/, "e-x.not.fyi", "electrum.backplanedns.org", "helicarrier.bauerj.eu", "electrum.vom-stausee.de", "electrum0.snel.it", "kirsche.emzy.de"}; - } - - public static String[] GetBitcoinTestNetServiceHosts() { - return new String[]{/*"testnetnode.arihanc.com"*/"testnet.hsmiths.com", "testnet.qtornado.com", "testnet1.bauerj.eu"}; - } - - public static Integer[] GetBitcoinServicePorts() { - return new Integer[]{8080,50001/* 8080*/, 50001, 50001, 50001, 50001, 50001, 50001, 50001, 50001}; - } - - public static Integer[] GetBitcoinTestNetServicePorts() { - return new Integer[]{/*51001*/53011, 51001, 50001}; - } - - static int serviceIndex = GetNextBitconMainNetServiceIndex(); - - static int dynamicIndex = GetNextBitconMainNetServiceIndex(); - - static int serviceIndexTestNet = GetNextBitconTestNetServiceIndex(); - - static int dynamicTestNetIndex = GetNextBitconTestNetServiceIndex(); - - static long lastChangeServiceIndex = 0; - - public static int GetNextBitconMainNetServiceIndex() { - Random r = new Random(); - return r.nextInt(GetBitcoinServiceHosts().length); - } - - public static void SelectNextBitconMainNetServiceIndex() { - //serviceIndex = GetNextBitconMainNetServiceIndex(); - serviceIndex++; - if (serviceIndex > GetBitcoinServiceHosts().length - 1) serviceIndex = 0; - } - - public static int GetNextBitconTestNetServiceIndex() { - Random r = new Random(); - return r.nextInt(GetBitcoinTestNetServiceHosts().length); - } - - public static void SelectNextBitconTestNetServiceIndex() { - //serviceIndexTestNet = GetNextBitconTestNetServiceIndex(); - - serviceIndexTestNet++; - if (serviceIndexTestNet > GetBitcoinTestNetServiceHosts().length - 1) serviceIndexTestNet = 0; - } - - public static void setNextDynamicIndex() { - //dynamicIndex = GetNextBitconMainNetServiceIndex(); - dynamicIndex++; - if (dynamicIndex > GetBitcoinServiceHosts().length - 1) dynamicIndex = 0; - } - - public static void setNextDynamicTestNet() { - //dynamicTestNetIndex = GetNextBitconTestNetServiceIndex(); - dynamicTestNetIndex++; - if (dynamicTestNetIndex > GetBitcoinTestNetServiceHosts().length - 1) dynamicTestNetIndex = 0; - - } - - public static void SelectNextBitconServiceIndex() { - long unixTime = System.currentTimeMillis() / 1000L; - long nextStampOffset = 5; - if(lastChangeServiceIndex == 0) - { - lastChangeServiceIndex = unixTime; - } - else if(lastChangeServiceIndex + nextStampOffset > unixTime ) - { - return; - } - - lastChangeServiceIndex = unixTime; - - SelectNextBitconMainNetServiceIndex(); - SelectNextBitconTestNetServiceIndex(); - } - - public static String getServiceHost(Tangem_Card mCard) { - switch (mCard.getBlockchain()) { - case Bitcoin: - return GetBitcoinServiceHosts()[serviceIndex]; //"hsmiths.changeip.net"; - case BitcoinTestNet: - return GetBitcoinTestNetServiceHosts()[serviceIndexTestNet]; //"testnetnode.arihanc.com"; - } - return null; - } - - public static String getNextServiceHost(Tangem_Card mCard) { - switch (mCard.getBlockchain()) { - case Bitcoin: { - setNextDynamicIndex(); - return GetBitcoinServiceHosts()[dynamicIndex]; - } - case BitcoinTestNet: { - setNextDynamicTestNet(); - return GetBitcoinTestNetServiceHosts()[dynamicTestNetIndex]; //"testnetnode.arihanc.com"; - } - //case BitcoinCash: - //{ - // return GetBitcoinCashServiceHosts()[0]; - //} - //case BitcoinCashTestNet: { - // return GetBitcoinCashTestNetServiceHosts()[0]; - //} - } - return null; - } - - public static int getNextServicePort(Tangem_Card mCard) { - switch (mCard.getBlockchain()) { - case Bitcoin: { - setNextDynamicIndex(); - return GetBitcoinServicePorts()[dynamicIndex];//8080; - } - case BitcoinTestNet: { - setNextDynamicTestNet(); - return GetBitcoinTestNetServicePorts()[dynamicTestNetIndex];//51001; - } - } - return 8080; - } - - public static int getServicePort(Tangem_Card mCard) { - switch (mCard.getBlockchain()) { - case Bitcoin: - return GetBitcoinServicePorts()[serviceIndex];//8080; - case BitcoinTestNet: - return GetBitcoinTestNetServicePorts()[serviceIndexTestNet];//51001; - } - return 8080; - } - - - public boolean InOutPutVisible() - { - return true; - } - - public boolean AwaitingConfirmation(Tangem_Card card) - { - return card.getBalanceUnconfirmed()!=0; - } - - public String GetBalanceWithAlter(Tangem_Card mCard) - { - return GetBalance(mCard); - } - - public boolean IsBalanceAlterNotZero(Tangem_Card card) - { - return true; - } - - public Long GetBalanceLong(Tangem_Card mCard) - { - return mCard.getBalance(); - } - - public boolean IsBalanceNotZero(Tangem_Card card) - { - return card.getBalance() > 0; - } - - public boolean CheckAmount(Tangem_Card card, String amount) throws Exception - { - DecimalFormat decimalFormat = GetDecimalFormat(); - BigDecimal amountValue = (BigDecimal) decimalFormat.parse(amount); - - // Convert Balance to BigDecimal - BigDecimal maxValue = new BigDecimal(GetBalanceValue(card)); - maxValue = maxValue.divide(new BigDecimal(1000)); - - //if (use_mCurrency) { - amountValue = amountValue.divide(new BigDecimal(1000)); - //} - - if (amountValue.compareTo(maxValue) > 0) { - return false; - } - - return true; - } - - public boolean HasBalanceInfo(Tangem_Card card) - { - return card.hasBalanceInfo(); - } - - public String GetBalanceCurrency(Tangem_Card card) - { - return "mBTC"; - } - - public boolean CheckUnspentTransaction(Tangem_Card mCard) - { - return mCard.getUnspentTransactions().size() != 0; - } - - public String GetFeeCurrency() - { - return "mBTC"; - } - - public boolean ValdateAddress(String address, Tangem_Card card){ - if(address == null || address.isEmpty()) - { - return false; - } - - if(address.length() < 25) - { - return false; - } - - if(address.length() > 35) - { - return false; - } - - if(!address.startsWith("1") && !address.startsWith("2") && !address.startsWith("3") && !address.startsWith("n") && !address.startsWith("m")) - { - return false; - } - - byte[] decAddress = Base58.decodeBase58(address); - - if(decAddress == null || decAddress.length == 0) - { - return false; - } - - byte[] rip = new byte[21]; - for(int i =0; i < 21; ++i) - { - rip[i] = decAddress[i]; - } - - byte[] kcv = CryptoUtil.doubleSha256(rip); - - for(int i =0; i < 4; ++i) - { - if(kcv[i] != decAddress[21+i]) - return false; - } - - if(card.getBlockchain()!=Blockchain.BitcoinTestNet && card.getBlockchain()!=Blockchain.Bitcoin) - { - return false; - } - - if(card.getBlockchain()==Blockchain.BitcoinTestNet && (address.startsWith("1") || address.startsWith("3"))) - { - return false; - } - - return true; - } - - - public int GetTokenDecimals(Tangem_Card card) - { - return 0; - } - - public String GetContractAddress(Tangem_Card card) - { - return ""; - } - - public boolean IsNeedCheckNode() - { - return true; - } - - public Uri getShareWalletURIExplorer(Tangem_Card mCard) - { - return Uri.parse((mCard.getBlockchain() == Blockchain.Bitcoin ? "https://blockchain.info/address/" : "https://testnet.blockchain.info/address/") + mCard.getWallet()); - } - public Uri getShareWalletURI(Tangem_Card mCard) - { - return Uri.parse("bitcoin:" + mCard.getWallet()); - } - public boolean CheckAmountValie(Tangem_Card mCard, String amountValue, String feeValue, Long minFeeInInternalUnits) - { - Long fee = null; - Long amount = null; - try { - amount = mCard.InternalUnitsFromString(amountValue); - fee = mCard.InternalUnitsFromString(feeValue); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - - if(fee == null || amount == null) - return false; - - if(fee == 0 || amount ==0) - return false; - - if(fee > amount) - return false; - - if(fee < minFeeInInternalUnits) - return false; - - return true; - } - - public String EvaluteFeeEquivalent(Tangem_Card mCard, String fee) - { - return GetAmountEqualentDescriptor(mCard, fee); - } - - @Override - public String GetBalanceEquivalent(Tangem_Card mCard) { - Double balance = Double.NaN; - try{ - Long val = mCard.getBalance(); - balance = mCard.AmountFromInternalUnits(val); - } - catch(Exception ex) - { - mCard.setRate(0); - } - - return mCard.getAmountEquivalentDescription(balance); - } - - public String GetBalance(Tangem_Card mCard) - { - if (mCard.hasBalanceInfo()) { - Double balance = mCard.AmountFromInternalUnits(mCard.getBalance()); - return mCard.getAmountDescription(balance); - } else { - return "-- -- -- " + mCard.getBlockchain().getCurrency(); - } - } - - public String GetBalanceValue(Tangem_Card mCard) - { - if (mCard.hasBalanceInfo()) { - Double balance = mCard.getBalance() / (mCard.getBlockchain().getMultiplier() / 1000.0); - - String output = FormatUtil.DoubleToString(balance); - //String pattern = "#0.000"; // If you like 4 zeros - //DecimalFormat myFormatter = new DecimalFormat(pattern); - //String output = myFormatter.format(balance); - return output; - - //return Double.toString(balance); - } - else - { - return "0"; - } - } - - public String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException { - - byte netSelectionByte; - switch (mCard.getBlockchain()) { - case Bitcoin: - netSelectionByte = (byte) 0x00; //0 - MainNet 0x6f - TestNet - break; - default : - netSelectionByte = (byte) 0x6f; //0 - MainNet 0x6f - TestNet - break; - } - - byte hash1[] = Util.calculateSHA256(pkUncompressed); - byte hash2[] = Util.calculateRIPEMD160(hash1); - - ByteBuffer BB = ByteBuffer.allocate(hash2.length + 1); - - BB.put(netSelectionByte); - BB.put(hash2); - - byte hash3[] = Util.calculateSHA256(BB.array()); - byte hash4[] = Util.calculateSHA256(hash3); - - BB = ByteBuffer.allocate(hash2.length + 5); - BB.put(netSelectionByte); //BB.put((byte) 0x6f); - BB.put(hash2); - BB.put(hash4[0]); - BB.put(hash4[1]); - BB.put(hash4[2]); - BB.put(hash4[3]); - - return org.bitcoinj.core.Base58.encode(BB.array()); - - } - - @Override - public String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception { - byte[] reversed=new byte[bytes.length]; - for(int i=0; i 0) { - return String.format("≈ USD %.2f", amount * rate); - } else { - return "≈ USD  ---"; - } - } - - public String GetAmountEqualentDescriptor(Tangem_Card mCard, String value) - { - return getAmountEquivalentDescriptionBTC(Double.parseDouble(value)/1000.0, mCard.getRate()); - } - - public byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception { - - String myAddress = mCard.getWallet(); - byte[] pbKey = mCard.getWalletPublicKey(); - String outputAddress = toValue; - String changeAddress = myAddress; - - // Build script for our address - List rawTxList = mCard.getUnspentTransactions(); - byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; - - // Collect unspent - ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); - - long fullAmount = 0; - for (int i = 0; i < unspentOutputs.size(); ++i) { - fullAmount += unspentOutputs.get(i).value; - } - - - long fees = FormatUtil.ConvertStringToLong(feeValue); - long amount = FormatUtil.ConvertStringToLong(amountValue); - amount = amount - fees; - - long change = fullAmount - fees - amount; - - if (amount + fees > fullAmount) { - throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); - } - - byte[][] dataForSign = new byte[unspentOutputs.size()][]; - - for (int i = 0; i < unspentOutputs.size(); ++i) { - byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, changeAddress, unspentOutputs, i, amount, change); - - byte[] hashData = Util.calculateSHA256(newTX); - byte[] doubleHashData = Util.calculateSHA256(hashData); - - unspentOutputs.get(i).bodyDoubleHash = doubleHashData; - unspentOutputs.get(i).bodyHash = hashData; - - if(mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw || mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw_Validated_By_Issuer) - { - dataForSign[i] = newTX; - } - else - { - dataForSign[i] = doubleHashData; - } - - } - - byte[] signFromCard = null; - if(mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw || mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw_Validated_By_Issuer) - { - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!"); - for (int i = 0; i < dataForSign.length; i++) { - if (i != 0 && dataForSign[0].length != dataForSign[i].length) - throw new Exception("Hashes length must be identical!"); - bs.write(dataForSign[i]); - } - signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), bs.toByteArray()).getTLV(TLV.Tag.TAG_Signature).Value; - } - else { - signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Hash_Validated_By_Issuer, null, mCard.getIssuer()).getTLV(TLV.Tag.TAG_Signature).Value; - // TODO slice signFromCard to hashes.length parts - } - - LastSignStorage.setLastSignDate(mCard.getWallet(), new Date()); - - - for (int i = 0; i < unspentOutputs.size(); ++i) { - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); - s = CryptoUtil.toCanonicalised(s); - byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey); - - unspentOutputs.get(i).scriptForBuild = encodingSign; - } - - byte[] realTX = BTCUtils.buildTXForSend(outputAddress, changeAddress, unspentOutputs, amount, change); - return realTX; - } -} +package com.tangem.wallet; + +import android.net.Uri; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.TLV; +import com.tangem.cardReader.Util; + +import java.io.ByteArrayOutputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Random; + +import static com.tangem.wallet.FormatUtil.GetDecimalFormat; + +/** + * Created by Ilia on 15.02.2018. + */ + +public class BtcEngine extends CoinEngine{ + public String GetNextNode(Tangem_Card mCard) + { + return getNextServiceHost(mCard); + } + public int GetNextNodePort(Tangem_Card mCard) + { + return getNextServicePort(mCard); + } + public String GetNode(Tangem_Card mCard) + { + return getServiceHost(mCard); + } + public int GetNodePort(Tangem_Card mCard) + { + return getServicePort(mCard); + } + + public void SwitchNode(Tangem_Card mCard) + { + SelectNextBitconServiceIndex(); + } + + public static String[] GetBitcoinServiceHosts() { + return new String[]{"vps.hsmiths.com", "tardis.bauerj.eu" /*"arihancckjge66iv.onion"*/, "electrumx.bot.nu","electrumx.hopto.org"/* "btc.asis.io"*/, "e-x.not.fyi", "electrum.backplanedns.org", "helicarrier.bauerj.eu", "electrum.vom-stausee.de", "electrum0.snel.it", "kirsche.emzy.de"}; + } + + public static String[] GetBitcoinTestNetServiceHosts() { + return new String[]{/*"testnetnode.arihanc.com"*/"testnet.hsmiths.com", "testnet.qtornado.com", "testnet1.bauerj.eu"}; + } + + public static Integer[] GetBitcoinServicePorts() { + return new Integer[]{8080,50001/* 8080*/, 50001, 50001, 50001, 50001, 50001, 50001, 50001, 50001}; + } + + public static Integer[] GetBitcoinTestNetServicePorts() { + return new Integer[]{/*51001*/53011, 51001, 50001}; + } + + static int serviceIndex = GetNextBitconMainNetServiceIndex(); + + static int dynamicIndex = GetNextBitconMainNetServiceIndex(); + + static int serviceIndexTestNet = GetNextBitconTestNetServiceIndex(); + + static int dynamicTestNetIndex = GetNextBitconTestNetServiceIndex(); + + static long lastChangeServiceIndex = 0; + + public static int GetNextBitconMainNetServiceIndex() { + Random r = new Random(); + return r.nextInt(GetBitcoinServiceHosts().length); + } + + public static void SelectNextBitconMainNetServiceIndex() { + //serviceIndex = GetNextBitconMainNetServiceIndex(); + serviceIndex++; + if (serviceIndex > GetBitcoinServiceHosts().length - 1) serviceIndex = 0; + } + + public static int GetNextBitconTestNetServiceIndex() { + Random r = new Random(); + return r.nextInt(GetBitcoinTestNetServiceHosts().length); + } + + public static void SelectNextBitconTestNetServiceIndex() { + //serviceIndexTestNet = GetNextBitconTestNetServiceIndex(); + + serviceIndexTestNet++; + if (serviceIndexTestNet > GetBitcoinTestNetServiceHosts().length - 1) serviceIndexTestNet = 0; + } + + public static void setNextDynamicIndex() { + //dynamicIndex = GetNextBitconMainNetServiceIndex(); + dynamicIndex++; + if (dynamicIndex > GetBitcoinServiceHosts().length - 1) dynamicIndex = 0; + } + + public static void setNextDynamicTestNet() { + //dynamicTestNetIndex = GetNextBitconTestNetServiceIndex(); + dynamicTestNetIndex++; + if (dynamicTestNetIndex > GetBitcoinTestNetServiceHosts().length - 1) dynamicTestNetIndex = 0; + + } + + public static void SelectNextBitconServiceIndex() { + long unixTime = System.currentTimeMillis() / 1000L; + long nextStampOffset = 5; + if(lastChangeServiceIndex == 0) + { + lastChangeServiceIndex = unixTime; + } + else if(lastChangeServiceIndex + nextStampOffset > unixTime ) + { + return; + } + + lastChangeServiceIndex = unixTime; + + SelectNextBitconMainNetServiceIndex(); + SelectNextBitconTestNetServiceIndex(); + } + + public static String getServiceHost(Tangem_Card mCard) { + switch (mCard.getBlockchain()) { + case Bitcoin: + return GetBitcoinServiceHosts()[serviceIndex]; //"hsmiths.changeip.net"; + case BitcoinTestNet: + return GetBitcoinTestNetServiceHosts()[serviceIndexTestNet]; //"testnetnode.arihanc.com"; + } + return null; + } + + public static String getNextServiceHost(Tangem_Card mCard) { + switch (mCard.getBlockchain()) { + case Bitcoin: { + setNextDynamicIndex(); + return GetBitcoinServiceHosts()[dynamicIndex]; + } + case BitcoinTestNet: { + setNextDynamicTestNet(); + return GetBitcoinTestNetServiceHosts()[dynamicTestNetIndex]; //"testnetnode.arihanc.com"; + } + //case BitcoinCash: + //{ + // return GetBitcoinCashServiceHosts()[0]; + //} + //case BitcoinCashTestNet: { + // return GetBitcoinCashTestNetServiceHosts()[0]; + //} + } + return null; + } + + public static int getNextServicePort(Tangem_Card mCard) { + switch (mCard.getBlockchain()) { + case Bitcoin: { + setNextDynamicIndex(); + return GetBitcoinServicePorts()[dynamicIndex];//8080; + } + case BitcoinTestNet: { + setNextDynamicTestNet(); + return GetBitcoinTestNetServicePorts()[dynamicTestNetIndex];//51001; + } + } + return 8080; + } + + public static int getServicePort(Tangem_Card mCard) { + switch (mCard.getBlockchain()) { + case Bitcoin: + return GetBitcoinServicePorts()[serviceIndex];//8080; + case BitcoinTestNet: + return GetBitcoinTestNetServicePorts()[serviceIndexTestNet];//51001; + } + return 8080; + } + + + public boolean InOutPutVisible() + { + return true; + } + + public boolean AwaitingConfirmation(Tangem_Card card) + { + return card.getBalanceUnconfirmed()!=0; + } + + public String GetBalanceWithAlter(Tangem_Card mCard) + { + return GetBalance(mCard); + } + + public boolean IsBalanceAlterNotZero(Tangem_Card card) + { + return true; + } + + public Long GetBalanceLong(Tangem_Card mCard) + { + return mCard.getBalance(); + } + + public boolean IsBalanceNotZero(Tangem_Card card) + { + return card.getBalance() > 0; + } + + public boolean CheckAmount(Tangem_Card card, String amount) throws Exception + { + DecimalFormat decimalFormat = GetDecimalFormat(); + BigDecimal amountValue = (BigDecimal) decimalFormat.parse(amount); + + // Convert Balance to BigDecimal + BigDecimal maxValue = new BigDecimal(GetBalanceValue(card)); + maxValue = maxValue.divide(new BigDecimal(1000)); + + //if (use_mCurrency) { + amountValue = amountValue.divide(new BigDecimal(1000)); + //} + + if (amountValue.compareTo(maxValue) > 0) { + return false; + } + + return true; + } + + public boolean HasBalanceInfo(Tangem_Card card) + { + return card.hasBalanceInfo(); + } + + public String GetBalanceCurrency(Tangem_Card card) + { + return "mBTC"; + } + + public boolean CheckUnspentTransaction(Tangem_Card mCard) + { + return mCard.getUnspentTransactions().size() != 0; + } + + public String GetFeeCurrency() + { + return "mBTC"; + } + + public boolean ValdateAddress(String address, Tangem_Card card){ + if(address == null || address.isEmpty()) + { + return false; + } + + if(address.length() < 25) + { + return false; + } + + if(address.length() > 35) + { + return false; + } + + if(!address.startsWith("1") && !address.startsWith("2") && !address.startsWith("3") && !address.startsWith("n") && !address.startsWith("m")) + { + return false; + } + + byte[] decAddress = Base58.decodeBase58(address); + + if(decAddress == null || decAddress.length == 0) + { + return false; + } + + byte[] rip = new byte[21]; + for(int i =0; i < 21; ++i) + { + rip[i] = decAddress[i]; + } + + byte[] kcv = CryptoUtil.doubleSha256(rip); + + for(int i =0; i < 4; ++i) + { + if(kcv[i] != decAddress[21+i]) + return false; + } + + if(card.getBlockchain()!=Blockchain.BitcoinTestNet && card.getBlockchain()!=Blockchain.Bitcoin) + { + return false; + } + + if(card.getBlockchain()==Blockchain.BitcoinTestNet && (address.startsWith("1") || address.startsWith("3"))) + { + return false; + } + + return true; + } + + + public int GetTokenDecimals(Tangem_Card card) + { + return 0; + } + + public String GetContractAddress(Tangem_Card card) + { + return ""; + } + + public boolean IsNeedCheckNode() + { + return true; + } + + public Uri getShareWalletURIExplorer(Tangem_Card mCard) + { + return Uri.parse((mCard.getBlockchain() == Blockchain.Bitcoin ? "https://blockchain.info/address/" : "https://testnet.blockchain.info/address/") + mCard.getWallet()); + } + public Uri getShareWalletURI(Tangem_Card mCard) + { + return Uri.parse("bitcoin:" + mCard.getWallet()); + } + public boolean CheckAmountValie(Tangem_Card mCard, String amountValue, String feeValue, Long minFeeInInternalUnits) + { + Long fee = null; + Long amount = null; + try { + amount = mCard.InternalUnitsFromString(amountValue); + fee = mCard.InternalUnitsFromString(feeValue); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + if(fee == null || amount == null) + return false; + + if(fee == 0 || amount ==0) + return false; + + if(fee > amount) + return false; + + if(fee < minFeeInInternalUnits) + return false; + + return true; + } + + public String EvaluteFeeEquivalent(Tangem_Card mCard, String fee) + { + return GetAmountEqualentDescriptor(mCard, fee); + } + + @Override + public String GetBalanceEquivalent(Tangem_Card mCard) { + Double balance = Double.NaN; + try{ + Long val = mCard.getBalance(); + balance = mCard.AmountFromInternalUnits(val); + } + catch(Exception ex) + { + mCard.setRate(0); + } + + return mCard.getAmountEquivalentDescription(balance); + } + + public String GetBalance(Tangem_Card mCard) + { + if (mCard.hasBalanceInfo()) { + Double balance = mCard.AmountFromInternalUnits(mCard.getBalance()); + return mCard.getAmountDescription(balance); + } else { + return "-- -- -- " + mCard.getBlockchain().getCurrency(); + } + } + + public String GetBalanceValue(Tangem_Card mCard) + { + if (mCard.hasBalanceInfo()) { + Double balance = mCard.getBalance() / (mCard.getBlockchain().getMultiplier() / 1000.0); + + String output = FormatUtil.DoubleToString(balance); + //String pattern = "#0.000"; // If you like 4 zeros + //DecimalFormat myFormatter = new DecimalFormat(pattern); + //String output = myFormatter.format(balance); + return output; + + //return Double.toString(balance); + } + else + { + return "0"; + } + } + + public String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException { + + byte netSelectionByte; + switch (mCard.getBlockchain()) { + case Bitcoin: + netSelectionByte = (byte) 0x00; //0 - MainNet 0x6f - TestNet + break; + default : + netSelectionByte = (byte) 0x6f; //0 - MainNet 0x6f - TestNet + break; + } + + byte hash1[] = Util.calculateSHA256(pkUncompressed); + byte hash2[] = Util.calculateRIPEMD160(hash1); + + ByteBuffer BB = ByteBuffer.allocate(hash2.length + 1); + + BB.put(netSelectionByte); + BB.put(hash2); + + byte hash3[] = Util.calculateSHA256(BB.array()); + byte hash4[] = Util.calculateSHA256(hash3); + + BB = ByteBuffer.allocate(hash2.length + 5); + BB.put(netSelectionByte); //BB.put((byte) 0x6f); + BB.put(hash2); + BB.put(hash4[0]); + BB.put(hash4[1]); + BB.put(hash4[2]); + BB.put(hash4[3]); + + return org.bitcoinj.core.Base58.encode(BB.array()); + + } + + @Override + public String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception { + byte[] reversed=new byte[bytes.length]; + for(int i=0; i 0) { + return String.format("≈ USD %.2f", amount * rate); + } else { + return "≈ USD  ---"; + } + } + + public String GetAmountEqualentDescriptor(Tangem_Card mCard, String value) + { + return getAmountEquivalentDescriptionBTC(Double.parseDouble(value)/1000.0, mCard.getRate()); + } + + public byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception { + + String myAddress = mCard.getWallet(); + byte[] pbKey = mCard.getWalletPublicKey(); + String outputAddress = toValue; + String changeAddress = myAddress; + + // Build script for our address + List rawTxList = mCard.getUnspentTransactions(); + byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; + + // Collect unspent + ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + + long fullAmount = 0; + for (int i = 0; i < unspentOutputs.size(); ++i) { + fullAmount += unspentOutputs.get(i).value; + } + + + long fees = FormatUtil.ConvertStringToLong(feeValue); + long amount = FormatUtil.ConvertStringToLong(amountValue); + amount = amount - fees; + + long change = fullAmount - fees - amount; + + if (amount + fees > fullAmount) { + throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); + } + + byte[][] dataForSign = new byte[unspentOutputs.size()][]; + + for (int i = 0; i < unspentOutputs.size(); ++i) { + byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, changeAddress, unspentOutputs, i, amount, change); + + byte[] hashData = Util.calculateSHA256(newTX); + byte[] doubleHashData = Util.calculateSHA256(hashData); + + unspentOutputs.get(i).bodyDoubleHash = doubleHashData; + unspentOutputs.get(i).bodyHash = hashData; + + if(mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw || mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw_Validated_By_Issuer) + { + dataForSign[i] = newTX; + } + else + { + dataForSign[i] = doubleHashData; + } + + } + + byte[] signFromCard = null; + if(mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw || mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Raw_Validated_By_Issuer) + { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!"); + for (int i = 0; i < dataForSign.length; i++) { + if (i != 0 && dataForSign[0].length != dataForSign[i].length) + throw new Exception("Hashes length must be identical!"); + bs.write(dataForSign[i]); + } + signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), bs.toByteArray()).getTLV(TLV.Tag.TAG_Signature).Value; + } + else { + signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, mCard.getSigningMethod() == Tangem_Card.SigningMethod.Sign_Hash_Validated_By_Issuer, null, mCard.getIssuer()).getTLV(TLV.Tag.TAG_Signature).Value; + // TODO slice signFromCard to hashes.length parts + } + + LastSignStorage.setLastSignDate(mCard.getWallet(), new Date()); + + + for (int i = 0; i < unspentOutputs.size(); ++i) { + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); + s = CryptoUtil.toCanonicalised(s); + byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey); + + unspentOutputs.get(i).scriptForBuild = encodingSign; + } + + byte[] realTX = BTCUtils.buildTXForSend(outputAddress, changeAddress, unspentOutputs, amount, change); + return realTX; + } +} diff --git a/app/src/main/java/com/tangem/wallet/ByteUtil.java b/app/src/main/java/com/tangem/wallet/ByteUtil.java index 2d27915fc0..d96cb2687a 100644 --- a/app/src/main/java/com/tangem/wallet/ByteUtil.java +++ b/app/src/main/java/com/tangem/wallet/ByteUtil.java @@ -1,39 +1,39 @@ -package com.tangem.wallet; - -public class ByteUtil { - - public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; - public static byte[] and(byte[] b1, byte[] b2) { - if (b1.length != b2.length) throw new RuntimeException("Array sizes differ"); - byte[] ret = new byte[b1.length]; - for (int i = 0; i < ret.length; i++) { - ret[i] = (byte) (b1[i] & b2[i]); - } - return ret; - } - - public static byte[] or(byte[] b1, byte[] b2) { - if (b1.length != b2.length) throw new RuntimeException("Array sizes differ"); - byte[] ret = new byte[b1.length]; - for (int i = 0; i < ret.length; i++) { - ret[i] = (byte) (b1[i] | b2[i]); - } - return ret; - } - - public static boolean isNullOrZeroArray(byte[] array){ - return (array == null) || (array.length == 0); - } - - public static boolean isSingleZero(byte[] array){ - return (array.length == 1 && array[0] == 0); - } - - public static int length(byte[]... bytes) { - int result = 0; - for (byte[] array : bytes) { - result += (array == null) ? 0 : array.length; - } - return result; - } +package com.tangem.wallet; + +public class ByteUtil { + + public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + public static byte[] and(byte[] b1, byte[] b2) { + if (b1.length != b2.length) throw new RuntimeException("Array sizes differ"); + byte[] ret = new byte[b1.length]; + for (int i = 0; i < ret.length; i++) { + ret[i] = (byte) (b1[i] & b2[i]); + } + return ret; + } + + public static byte[] or(byte[] b1, byte[] b2) { + if (b1.length != b2.length) throw new RuntimeException("Array sizes differ"); + byte[] ret = new byte[b1.length]; + for (int i = 0; i < ret.length; i++) { + ret[i] = (byte) (b1[i] | b2[i]); + } + return ret; + } + + public static boolean isNullOrZeroArray(byte[] array){ + return (array == null) || (array.length == 0); + } + + public static boolean isSingleZero(byte[] array){ + return (array.length == 1 && array[0] == 0); + } + + public static int length(byte[]... bytes) { + int result = 0; + for (byte[] array : bytes) { + result += (array == null) ? 0 : array.length; + } + return result; + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/CardInfoActivity.java b/app/src/main/java/com/tangem/wallet/CardInfoActivity.java index 78f53030f1..d80b2a306b 100644 --- a/app/src/main/java/com/tangem/wallet/CardInfoActivity.java +++ b/app/src/main/java/com/tangem/wallet/CardInfoActivity.java @@ -1,122 +1,122 @@ -package com.tangem.wallet; - -import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -import android.support.v7.app.AppCompatActivity; -import android.view.Menu; -import android.view.MenuItem; - -public class CardInfoActivity extends AppCompatActivity implements WalletInfoFragment.OnFragmentInteractionListener { - - /** - * The {@link android.support.v4.view.PagerAdapter} that will provide - * fragments for each of the sections. We use a - * {@link FragmentPagerAdapter} derivative, which will keep every - * loaded fragment in memory. If this becomes too memory intensive, it - * may be best to switch to a - * {@link android.support.v4.app.FragmentStatePagerAdapter}. - */ - private SectionsPagerAdapter mSectionsPagerAdapter; - - /** - * The {@link ViewPager} that will host the section contents. - */ - private ViewPager mViewPager; - - - private Tangem_Card mCard; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_card_info); - - // Create the adapter that will return a fragment for each of the three - // primary sections of the activity. - mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); - - // Set up the ViewPager with the sections adapter. - mViewPager = (ViewPager) findViewById(R.id.container); - mViewPager.setAdapter(mSectionsPagerAdapter); - - TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); - tabLayout.setupWithViewPager(mViewPager); - - String UID = getIntent().getStringExtra("UID"); - mCard = new Tangem_Card(UID); - mCard.LoadFromBundle(getIntent().getBundleExtra("Card")); - - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_card_info, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == R.id.action_settings) { - return true; - } - - return super.onOptionsItemSelected(item); - } - - - /** - * A {@link FragmentPagerAdapter} that returns a fragment corresponding to - * one of the sections/tabs/pages. - */ - public class SectionsPagerAdapter extends FragmentPagerAdapter { - - public SectionsPagerAdapter(FragmentManager fm) { - super(fm); - } - - @Override - public Fragment getItem(int position) { - // getItem is called to instantiate the fragment for the given page. - // Return a PlaceholderFragment (defined as a static inner class below). - if (position == 0) { - return WalletInfoFragment.newInstance(mCard); - } /*else if (position == 1) { - return WalletUnspentFragment.newInstance(mCard); - } else if (position == 2) { - return WalletHistoryFragment.newInstance(mCard); - }*/ - return null; - } - - @Override - public int getCount() { - // Show 3 total pages. - return 3; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return "Wallet info"; - case 1: - return "Unspent"; - case 2: - return "History"; - } - return null; - } - } -} +package com.tangem.wallet; + +import android.os.Bundle; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +public class CardInfoActivity extends AppCompatActivity implements WalletInfoFragment.OnFragmentInteractionListener { + + /** + * The {@link android.support.v4.view.PagerAdapter} that will provide + * fragments for each of the sections. We use a + * {@link FragmentPagerAdapter} derivative, which will keep every + * loaded fragment in memory. If this becomes too memory intensive, it + * may be best to switch to a + * {@link android.support.v4.app.FragmentStatePagerAdapter}. + */ + private SectionsPagerAdapter mSectionsPagerAdapter; + + /** + * The {@link ViewPager} that will host the section contents. + */ + private ViewPager mViewPager; + + + private Tangem_Card mCard; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_card_info); + + // Create the adapter that will return a fragment for each of the three + // primary sections of the activity. + mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); + + // Set up the ViewPager with the sections adapter. + mViewPager = (ViewPager) findViewById(R.id.container); + mViewPager.setAdapter(mSectionsPagerAdapter); + + TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); + tabLayout.setupWithViewPager(mViewPager); + + String UID = getIntent().getStringExtra("UID"); + mCard = new Tangem_Card(UID); + mCard.LoadFromBundle(getIntent().getBundleExtra("Card")); + + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_card_info, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + + /** + * A {@link FragmentPagerAdapter} that returns a fragment corresponding to + * one of the sections/tabs/pages. + */ + public class SectionsPagerAdapter extends FragmentPagerAdapter { + + public SectionsPagerAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public Fragment getItem(int position) { + // getItem is called to instantiate the fragment for the given page. + // Return a PlaceholderFragment (defined as a static inner class below). + if (position == 0) { + return WalletInfoFragment.newInstance(mCard); + } /*else if (position == 1) { + return WalletUnspentFragment.newInstance(mCard); + } else if (position == 2) { + return WalletHistoryFragment.newInstance(mCard); + }*/ + return null; + } + + @Override + public int getCount() { + // Show 3 total pages. + return 3; + } + + @Override + public CharSequence getPageTitle(int position) { + switch (position) { + case 0: + return "Wallet info"; + case 1: + return "Unspent"; + case 2: + return "History"; + } + return null; + } + } +} diff --git a/app/src/main/java/com/tangem/wallet/CardUnspentListAdapter.java b/app/src/main/java/com/tangem/wallet/CardUnspentListAdapter.java index b5191ea7d1..19d57e41ff 100644 --- a/app/src/main/java/com/tangem/wallet/CardUnspentListAdapter.java +++ b/app/src/main/java/com/tangem/wallet/CardUnspentListAdapter.java @@ -1,83 +1,83 @@ -package com.tangem.wallet; - -import android.content.Context; -import android.os.Build; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.TextView; - -import static android.text.Html.FROM_HTML_MODE_COMPACT; - -/** - * Created by dvol on 17.07.2017. - */ - -public class CardUnspentListAdapter extends BaseAdapter { - private LayoutInflater mLayoutInflater; - private Context mContext; - private Tangem_Card mCard; - - public CardUnspentListAdapter(LayoutInflater layoutInflater, Tangem_Card card) { - mLayoutInflater = layoutInflater; - mContext = layoutInflater.getContext(); - mCard = card; - } - - @Override - public int getCount() { - if (mCard != null && mCard.getUnspentTransactions() != null) - return mCard.getUnspentTransactions().size(); - return 0; - } - - @Override - public Object getItem(int i) { - if (mCard != null && mCard.getUnspentTransactions() != null) - return mCard.getUnspentTransactions().get(i); - return null; - } - - @Override - public long getItemId(int i) { - if (mCard != null && mCard.getUnspentTransactions() != null) - return mCard.getUnspentTransactions().get(i).txID.hashCode(); - return 0; - } - - @Override - public View getView(int position, View convertView, ViewGroup viewGroup) { - if (convertView == null) { - convertView = mLayoutInflater.inflate(R.layout.card_unspent_list_item, viewGroup, - false); - } - TextView tvItem = (TextView) convertView.findViewById(R.id.tvItem); - - Tangem_Card.UnspentTransaction unspentTransaction = (Tangem_Card.UnspentTransaction) getItem(position); - - String html=String.format("%d mBTC
%s", unspentTransaction.Amount, unspentTransaction.txID); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - tvItem.setText(Html.fromHtml(html, FROM_HTML_MODE_COMPACT)); - } else { - tvItem.setText(Html.fromHtml(html.toString())); - } - return convertView; - } - - public void Clear() { - mCard.getUnspentTransactions().clear(); - notifyDataSetChanged(); - } - - public void UpdateUnspent(String tx_hash, int value, int height) { - Tangem_Card.UnspentTransaction newUT=new Tangem_Card.UnspentTransaction(); - newUT.txID=tx_hash; - newUT.Amount=value; - newUT.Height=height; - mCard.getUnspentTransactions().add(newUT); - notifyDataSetChanged(); - } -} +package com.tangem.wallet; + +import android.content.Context; +import android.os.Build; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import static android.text.Html.FROM_HTML_MODE_COMPACT; + +/** + * Created by dvol on 17.07.2017. + */ + +public class CardUnspentListAdapter extends BaseAdapter { + private LayoutInflater mLayoutInflater; + private Context mContext; + private Tangem_Card mCard; + + public CardUnspentListAdapter(LayoutInflater layoutInflater, Tangem_Card card) { + mLayoutInflater = layoutInflater; + mContext = layoutInflater.getContext(); + mCard = card; + } + + @Override + public int getCount() { + if (mCard != null && mCard.getUnspentTransactions() != null) + return mCard.getUnspentTransactions().size(); + return 0; + } + + @Override + public Object getItem(int i) { + if (mCard != null && mCard.getUnspentTransactions() != null) + return mCard.getUnspentTransactions().get(i); + return null; + } + + @Override + public long getItemId(int i) { + if (mCard != null && mCard.getUnspentTransactions() != null) + return mCard.getUnspentTransactions().get(i).txID.hashCode(); + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup viewGroup) { + if (convertView == null) { + convertView = mLayoutInflater.inflate(R.layout.card_unspent_list_item, viewGroup, + false); + } + TextView tvItem = (TextView) convertView.findViewById(R.id.tvItem); + + Tangem_Card.UnspentTransaction unspentTransaction = (Tangem_Card.UnspentTransaction) getItem(position); + + String html=String.format("%d mBTC
%s", unspentTransaction.Amount, unspentTransaction.txID); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + tvItem.setText(Html.fromHtml(html, FROM_HTML_MODE_COMPACT)); + } else { + tvItem.setText(Html.fromHtml(html.toString())); + } + return convertView; + } + + public void Clear() { + mCard.getUnspentTransactions().clear(); + notifyDataSetChanged(); + } + + public void UpdateUnspent(String tx_hash, int value, int height) { + Tangem_Card.UnspentTransaction newUT=new Tangem_Card.UnspentTransaction(); + newUT.txID=tx_hash; + newUT.Amount=value; + newUT.Height=height; + mCard.getUnspentTransactions().add(newUT); + notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/com/tangem/wallet/CoinEngine.java b/app/src/main/java/com/tangem/wallet/CoinEngine.java index f56c71ab12..9796775fcb 100644 --- a/app/src/main/java/com/tangem/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/wallet/CoinEngine.java @@ -1,82 +1,82 @@ -package com.tangem.wallet; - -import android.net.Uri; - -import com.tangem.cardReader.CardProtocol; - -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; - -/** - * Created by Ilia on 15.02.2018. - */ - -public abstract class CoinEngine { - - public abstract String GetNextNode(Tangem_Card mCard); - - public abstract int GetNextNodePort(Tangem_Card mCard); - - public abstract String GetNode(Tangem_Card mCard); - - public abstract int GetNodePort(Tangem_Card mCard); - - public abstract void SwitchNode(Tangem_Card mCard); - - public abstract boolean AwaitingConfirmation(Tangem_Card card); - - public abstract boolean HasBalanceInfo(Tangem_Card card); - - public abstract boolean IsBalanceNotZero(Tangem_Card card); - - public abstract boolean IsBalanceAlterNotZero(Tangem_Card card); - - public abstract boolean CheckAmount(Tangem_Card card, String amount) throws Exception; - - public abstract int GetTokenDecimals(Tangem_Card card); - - public abstract String GetContractAddress(Tangem_Card card); - - public abstract byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception; - - public abstract boolean CheckUnspentTransaction(Tangem_Card mCard); - - public abstract Uri getShareWalletURIExplorer(Tangem_Card mCard); - - public abstract Long GetBalanceLong(Tangem_Card mCard); - - public abstract Uri getShareWalletURI(Tangem_Card mCard); - - public abstract String EvaluteFeeEquivalent(Tangem_Card mCard, String fee); - - public abstract boolean CheckAmountValie(Tangem_Card mCard, String amount, String fee, Long minFeeInInternalUnits); - - public abstract boolean InOutPutVisible(); - - public abstract String GetBalance(Tangem_Card mCard); - - public abstract String GetBalanceWithAlter(Tangem_Card mCard); - - public abstract String GetBalanceCurrency(Tangem_Card card); - - public abstract String GetFeeCurrency(); - - public abstract boolean IsNeedCheckNode(); - - public abstract String GetBalanceEquivalent(Tangem_Card mCard); - - public abstract String GetBalanceValue(Tangem_Card mCard); - - public abstract String GetAmountDescription(Tangem_Card mCard, String amount) throws Exception; - - public abstract String GetAmountEqualentDescriptor(Tangem_Card mCard, String value); - - public abstract boolean ValdateAddress(String address, Tangem_Card catd); - - public abstract String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException; - - public abstract String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception; - - public abstract byte[] ConvertAmountToByteArray(Tangem_Card mCard, String amount) throws Exception; - +package com.tangem.wallet; + +import android.net.Uri; + +import com.tangem.cardReader.CardProtocol; + +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +/** + * Created by Ilia on 15.02.2018. + */ + +public abstract class CoinEngine { + + public abstract String GetNextNode(Tangem_Card mCard); + + public abstract int GetNextNodePort(Tangem_Card mCard); + + public abstract String GetNode(Tangem_Card mCard); + + public abstract int GetNodePort(Tangem_Card mCard); + + public abstract void SwitchNode(Tangem_Card mCard); + + public abstract boolean AwaitingConfirmation(Tangem_Card card); + + public abstract boolean HasBalanceInfo(Tangem_Card card); + + public abstract boolean IsBalanceNotZero(Tangem_Card card); + + public abstract boolean IsBalanceAlterNotZero(Tangem_Card card); + + public abstract boolean CheckAmount(Tangem_Card card, String amount) throws Exception; + + public abstract int GetTokenDecimals(Tangem_Card card); + + public abstract String GetContractAddress(Tangem_Card card); + + public abstract byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception; + + public abstract boolean CheckUnspentTransaction(Tangem_Card mCard); + + public abstract Uri getShareWalletURIExplorer(Tangem_Card mCard); + + public abstract Long GetBalanceLong(Tangem_Card mCard); + + public abstract Uri getShareWalletURI(Tangem_Card mCard); + + public abstract String EvaluteFeeEquivalent(Tangem_Card mCard, String fee); + + public abstract boolean CheckAmountValie(Tangem_Card mCard, String amount, String fee, Long minFeeInInternalUnits); + + public abstract boolean InOutPutVisible(); + + public abstract String GetBalance(Tangem_Card mCard); + + public abstract String GetBalanceWithAlter(Tangem_Card mCard); + + public abstract String GetBalanceCurrency(Tangem_Card card); + + public abstract String GetFeeCurrency(); + + public abstract boolean IsNeedCheckNode(); + + public abstract String GetBalanceEquivalent(Tangem_Card mCard); + + public abstract String GetBalanceValue(Tangem_Card mCard); + + public abstract String GetAmountDescription(Tangem_Card mCard, String amount) throws Exception; + + public abstract String GetAmountEqualentDescriptor(Tangem_Card mCard, String value); + + public abstract boolean ValdateAddress(String address, Tangem_Card catd); + + public abstract String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException; + + public abstract String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception; + + public abstract byte[] ConvertAmountToByteArray(Tangem_Card mCard, String amount) throws Exception; + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/CoinEngineFactory.java b/app/src/main/java/com/tangem/wallet/CoinEngineFactory.java index 92c91713e9..ba8a649112 100644 --- a/app/src/main/java/com/tangem/wallet/CoinEngineFactory.java +++ b/app/src/main/java/com/tangem/wallet/CoinEngineFactory.java @@ -1,23 +1,23 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 15.02.2018. - */ - -public class CoinEngineFactory { - public static CoinEngine Create(Blockchain chain) - { - if(Blockchain.BitcoinCash == chain || Blockchain.BitcoinCashTestNet == chain) { - return new BtcCashEngine(); - }else if(Blockchain.Bitcoin == chain || Blockchain.BitcoinTestNet == chain) { - return new BtcEngine(); //TODO: ВРЕМЕНГГО!!!! - }else if(Blockchain.Ethereum == chain || Blockchain.EthereumTestNet == chain) { - return new EthEngine(); - } - else if(Blockchain.Token == chain) { - return new TokenEngine(); - } else { - return null; - } - } -} +package com.tangem.wallet; + +/** + * Created by Ilia on 15.02.2018. + */ + +public class CoinEngineFactory { + public static CoinEngine Create(Blockchain chain) + { + if(Blockchain.BitcoinCash == chain || Blockchain.BitcoinCashTestNet == chain) { + return new BtcCashEngine(); + }else if(Blockchain.Bitcoin == chain || Blockchain.BitcoinTestNet == chain) { + return new BtcEngine(); //TODO: ВРЕМЕНГГО!!!! + }else if(Blockchain.Ethereum == chain || Blockchain.EthereumTestNet == chain) { + return new EthEngine(); + } + else if(Blockchain.Token == chain) { + return new TokenEngine(); + } else { + return null; + } + } +} diff --git a/app/src/main/java/com/tangem/wallet/ConfirmPaymentActivity.java b/app/src/main/java/com/tangem/wallet/ConfirmPaymentActivity.java index 0bb59e9f6b..9697c35770 100644 --- a/app/src/main/java/com/tangem/wallet/ConfirmPaymentActivity.java +++ b/app/src/main/java/com/tangem/wallet/ConfirmPaymentActivity.java @@ -1,732 +1,732 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.AsyncTask; -import android.os.Bundle; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.app.AppCompatActivity; -import android.text.Editable; -import android.text.Html; -import android.text.Spanned; -import android.text.TextWatcher; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.RadioGroup; -import android.widget.TextView; -import android.widget.Toast; - -import com.tangem.cardReader.NfcManager; -import com.tangem.cardReader.Util; - -import org.json.JSONException; - -import java.io.IOException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.List; - -public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback { - - private static final int REQUEST_CODE_SIGN_PAYMENT = 1; - private static final int REQUEST_CODE_REQUEST_PIN2 = 2; - Button btnSend; - boolean feeRequestSuccess = false; - boolean balanceRequestSuccess = false; - EditText etWallet; - TextView tvCardID, tvBalance, tvCurrency, tvCurrency2, tvBalanceEquivalent, tvAmountEquivalent, tvFeeEquivalent; - EditText etAmount; - EditText etFee; - ImageView ivCamera; - Tangem_Card mCard; - RadioGroup rgFee; - String minFee = null, maxFee = null, normalFee = null; - Long minFeeInInternalUnits = 0L; - private NfcManager mNfcManager; - int requestPIN2Count = 0; - ProgressBar progressBar; - boolean nodeCheck = false; - Date dtVerifyed=null; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_confirm_payment); - - MainActivity.commonInit(getApplicationContext()); - mNfcManager = new NfcManager(this, this); - - mCard = new Tangem_Card(getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); - - progressBar = findViewById(R.id.progressBar); - - btnSend = findViewById(R.id.btnSend); - etWallet = findViewById(R.id.etWallet); - tvCardID = findViewById(R.id.tvCardID); - tvBalance = findViewById(R.id.tvBalance); - tvCurrency = findViewById(R.id.tvCurrency); - tvCurrency2 = findViewById(R.id.tvCurrency2); - etAmount = findViewById(R.id.etAmount); - etFee = findViewById(R.id.etFee); - tvBalanceEquivalent = findViewById(R.id.tvBalanceEquivalent); - tvAmountEquivalent = findViewById(R.id.tvAmountEquivalent); - - tvFeeEquivalent = findViewById(R.id.tvFeeEquivalent); - ivCamera = findViewById(R.id.ivCamera); - - rgFee = findViewById(R.id.rgFee); - rgFee.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - doSetFee(checkedId); - } - }); - - etAmount.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - try { - - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - tvAmountEquivalent.setText(engine.GetAmountEqualentDescriptor(mCard, etAmount.getText().toString())); - if (!mCard.getAmountEquivalentDescriptionAvailable()) { - tvAmountEquivalent.setError("Service unavailable"); - } else { - tvAmountEquivalent.setError(null); - } - } catch (Exception e) { - e.printStackTrace(); - tvAmountEquivalent.setText(""); - } - } - - @Override - public void afterTextChanged(Editable s) { - - } - }); - - etFee.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - try { - - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - String eqFee = engine.EvaluteFeeEquivalent(mCard, etFee.getText().toString()); - tvFeeEquivalent.setText(eqFee); - - if (!mCard.getAmountEquivalentDescriptionAvailable()) { - tvFeeEquivalent.setError("Service unavailable"); - } else { - tvFeeEquivalent.setError(null); - } - } catch (Exception e) { - e.printStackTrace(); - tvFeeEquivalent.setText(""); - } - } - - @Override - public void afterTextChanged(Editable s) { - - } - }); - - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - //tvBalance.setText(engine.GetBalanceWithAlter(mCard)); - if (mCard.getBlockchain() == Blockchain.Token) { - Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard)); - tvBalance.setText(html); - } else { - tvBalance.setText(engine.GetBalanceWithAlter(mCard)); - } - etAmount.setText(getIntent().getStringExtra("Amount")); - tvCurrency.setText(engine.GetBalanceCurrency(mCard)); - tvCurrency2.setText(engine.GetFeeCurrency()); - - tvCardID.setText(mCard.getCIDDescription()); - - //tvBalanceEquivalent.setText(mCard.getBalanceEquivalentDescription()); - tvBalanceEquivalent.setText(engine.GetBalanceEquivalent(mCard)); - if (!mCard.getAmountEquivalentDescriptionAvailable()) { - tvBalanceEquivalent.setError("Service unavailable"); - } else { - tvBalanceEquivalent.setError(null); - } - - etWallet.setText(getIntent().getStringExtra("Wallet")); - - etFee.setText("?"); - - btnSend.setVisibility(View.INVISIBLE); - feeRequestSuccess = false; - balanceRequestSuccess = false; - btnSend.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - - Calendar calendar=Calendar.getInstance(); - calendar.add(Calendar.MINUTE,-1); - - if( dtVerifyed==null || dtVerifyed.before(calendar.getTime()) ) { - FinishActivityWithError(Activity.RESULT_CANCELED, "The obtained data is outdated! Try again"); - return; - } - - CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain()); - - if (engineCoin.IsNeedCheckNode() && !nodeCheck) { - Toast.makeText(getBaseContext(), "Cannot reach current active blockchain node. Try again", Toast.LENGTH_LONG).show(); - return; - } - String txFee = etFee.getText().toString(); - String txAmount = etAmount.getText().toString(); - - - if (!engineCoin.HasBalanceInfo(mCard)) { - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes"); - return; - } else if (!engineCoin.IsBalanceNotZero(mCard)) { - FinishActivityWithError(Activity.RESULT_CANCELED, "The wallet is empty"); - return; - } else if(!engineCoin.CheckUnspentTransaction(mCard)) { - //else if (mCard.getUnspentTransactions().size() == 0 && mCard.getBlockchain() != Blockchain.Ethereum) { - FinishActivityWithError(Activity.RESULT_CANCELED, "Please wait for confirmation of incoming transaction"); - return; - } - - if(!engineCoin.CheckAmountValie(mCard, txAmount, txFee, minFeeInInternalUnits)) - { - FinishActivityWithError(Activity.RESULT_CANCELED, "Fee exceeds payment amount. Enter correct value and repeat sending."); - return; - } - - requestPIN2Count = 0; - Intent intent = new Intent(getBaseContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2); - } - }); - - if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet || mCard.getBlockchain() == Blockchain.Token) { - ETHRequestTask task = new ETHRequestTask(mCard.getBlockchain()); - Infura_Request req = Infura_Request.GetGasPrise(mCard.getWallet()); - req.setID(67); - req.setBlockchain(mCard.getBlockchain()); - rgFee.setEnabled(false); - task.execute(req); - } else { - - rgFee.setEnabled(true); - - SharedData data = new SharedData(SharedData.COUNT_REQUEST); - - CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain()); - - for(int i =0 ; i < data.allRequest; ++i) { - - String nodeAddress = engineCoin.GetNextNode(mCard); - int nodePort = engineCoin.GetNextNodePort(mCard); - //ConnectTask connectTaskEx = new ConnectTask(Blockchain.getNextServiceHost(mCard), Blockchain.getNextServicePort(mCard), data); - ConnectTask connectTaskEx = new ConnectTask(nodeAddress, nodePort, data); - - //connectTaskEx.execute(Electrum_Request.CheckBalance(mCard.getWallet())); - connectTaskEx.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR,Electrum_Request.CheckBalance(mCard.getWallet())); - } - - String nodeAddress = engineCoin.GetNode(mCard); - int nodePort = engineCoin.GetNodePort(mCard); - ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort, data); - - //ConnectTask connectTask = new ConnectTask(Blockchain.getServiceHost(mCard), Blockchain.getServicePort(mCard)); - - connectTask.execute(/*Electrum_Request.CheckBalance(mCard.getWallet()), */Electrum_Request.GetFee(mCard.getWallet())); - - int calcSize = 256; - try { - calcSize = BuildSize(etWallet.getText().toString(), "0.00", etAmount.getText().toString()); - } catch (Exception ex) { - Log.e("Build Fee error", ex.getMessage()); - } - - SharedData sharedFee = new SharedData(SharedData.COUNT_REQUEST); - - progressBar.setVisibility(View.VISIBLE); - for(int i = 0; i < SharedData.COUNT_REQUEST; ++i) - { - - ConnectFeeTask feeTask = new ConnectFeeTask(sharedFee); - - feeTask.execute(Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.NORMAL), - Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.MINIMAL), - Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.PRIORITY)); - } - } - - } - - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - Intent intent = new Intent(); - intent.putExtra("message", "Operation canceled"); - setResult(Activity.RESULT_CANCELED, intent); - finish(); - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_CODE_SIGN_PAYMENT) { - if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { - Tangem_Card updatedCard=new Tangem_Card(data.getStringExtra("UID")); - updatedCard.LoadFromBundle(data.getBundleExtra("Card")); - mCard=updatedCard; - } - if (resultCode == SignPaymentActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) { - requestPIN2Count++; - Intent intent = new Intent(getBaseContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2); - return; - } - setResult(resultCode, data); - finish(); - } else if (requestCode == REQUEST_CODE_REQUEST_PIN2) { - if (resultCode == Activity.RESULT_OK) { - Intent intent = new Intent(getBaseContext(), SignPaymentActivity.class); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - intent.putExtra("Wallet", etWallet.getText().toString()); - intent.putExtra("Amount", etAmount.getText().toString()); - intent.putExtra("Fee", etFee.getText().toString()); - startActivityForResult(intent, REQUEST_CODE_SIGN_PAYMENT); - } else { - Toast.makeText(getBaseContext(), "PIN2 is required to sign the payment", Toast.LENGTH_LONG).show(); - } - } - } - - void FinishActivityWithError(int errorCode, String message) { - //Snackbar.make(etFee, message, Snackbar.LENGTH_LONG).show(); - Intent intent = new Intent(); - intent.putExtra("message", message); - setResult(errorCode, intent); - finish(); - } - - @Override - public void onTagDiscovered(Tag tag) { - try { - Log.w(getClass().getName(), "Ignore discovered tag!"); - mNfcManager.IgnoreTag(tag); - } catch (IOException e) { - e.printStackTrace(); - } - } - - int BuildSize(String outputAddress, String outFee, String outAmount) throws Exception { - String myAddress = mCard.getWallet(); - String changeAddress = myAddress; //"n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"; - byte[] pbKey = mCard.getWalletPublicKey(); - byte[] pbComprKey = mCard.getWalletPublicKeyRar(); - - // Build script for our address - List rawTxList = mCard.getUnspentTransactions(); - byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; - - // Collect unspent - ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); - - long fullAmount = 0; - for (int i = 0; i < unspentOutputs.size(); ++i) { - fullAmount += unspentOutputs.get(i).value; - } - - // Get first unspent - UnspentOutputInfo outPut = unspentOutputs.get(0); - int outPutIndex = outPut.outputIndex; - - // get prev TX id; - String prevTXID = rawTxList.get(0).txID;//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2"; - - long fees = FormatUtil.ConvertStringToLong(outFee); - long amount = FormatUtil.ConvertStringToLong(outAmount); - amount = amount - fees; - - long change = fullAmount - fees - amount; - - if (amount + fees > fullAmount) { - throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); - } - - byte[][] hashesForSign = new byte[unspentOutputs.size()][]; - - for (int i = 0; i < unspentOutputs.size(); ++i) { - byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, changeAddress, unspentOutputs, i, amount, change); - - byte[] hashData = Util.calculateSHA256(newTX); - byte[] doubleHashData = Util.calculateSHA256(hashData); - - Log.e("TX_BODY_1", BTCUtils.toHex(newTX)); - Log.e("TX_HASH_1", BTCUtils.toHex(hashData)); - Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData)); - - unspentOutputs.get(i).bodyDoubleHash = doubleHashData; - unspentOutputs.get(i).bodyHash = hashData; - - hashesForSign[i] = doubleHashData; - } - - byte[] signFromCard = new byte[64 * unspentOutputs.size()]; - - for (int i = 0; i < unspentOutputs.size(); ++i) { - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); - byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey); - unspentOutputs.get(i).scriptForBuild = encodingSign; - } - - byte[] realTX = BTCUtils.buildTXForSend(outputAddress, changeAddress, unspentOutputs, amount, change); - - return realTX.length; - } - - private class ConnectTask extends Electrum_Task { - public ConnectTask(String host, int port) { - super(host, port); - } - - public ConnectTask(String host, int port, SharedData sharedData) { - super(host, port, sharedData); - } - @Override - protected void onProgressUpdate(Integer... values) { - super.onProgressUpdate(values); - } - - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - for (Electrum_Request request : requests) { - try { - if (request.error == null) { - if (request.isMethod(Electrum_Request.METHOD_GetBalance)) { - try { - etFee.setText("--"); - - //String mWalletAddress = request.getParams().getString(0); - if ((request.getResult().getInt("confirmed") + request.getResult().getInt("unconfirmed")) / mCard.getBlockchain().getMultiplier() * 1000000.0 < Float.parseFloat(etAmount.getText().toString())) { - etFee.setError("Not enough funds"); - balanceRequestSuccess = false; - btnSend.setVisibility(View.INVISIBLE); - dtVerifyed=null; - nodeCheck = false; - } else { - etFee.setError(null); - balanceRequestSuccess = true; - if(feeRequestSuccess && balanceRequestSuccess) { - btnSend.setVisibility(View.VISIBLE); - - } - dtVerifyed=new Date(); - nodeCheck = true; - } - } catch (JSONException e) { - if(sharedCounter != null) - { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if(errCounter >= sharedCounter.allRequest) - { - e.printStackTrace(); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes"); - } - } - else - { - e.printStackTrace(); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes"); - } - } - } else if (request.isMethod(Electrum_Request.METHOD_GetFee)) { - if (request.getResultString() == "-1") { - etFee.setText("3"); - } - } - } else { -// etFee.setError(request.error); -// btnSend.setVisibility(View.INVISIBLE); - if(sharedCounter != null) - { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if(errCounter >= sharedCounter.allRequest) - { - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - } - else - { - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - return; - } - } catch (JSONException e) { - if(sharedCounter != null) - { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if(errCounter >= sharedCounter.allRequest) - { - e.printStackTrace(); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - - } - } - else - { - e.printStackTrace(); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - } - } - - } - } - - private class ETHRequestTask extends Infura_Task { - ETHRequestTask(Blockchain blockchain){ - super(blockchain); - } - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - for (Infura_Request request : requests) { - try { - Long price = 0L; - if (request.error == null) { - - if (request.isMethod(Infura_Request.METHOD_ETH_GetGasPrice)) { - try { - String gasPrice = request.getResultString(); - gasPrice = gasPrice.substring(2); - BigInteger l = new BigInteger(gasPrice, 16); - - BigInteger m = mCard.getBlockchain() == Blockchain.Token ? BigInteger.valueOf(55000) : BigInteger.valueOf(21000); - l = l.multiply(m); - String feeInGwei = mCard.getAmountInGwei(String.valueOf(l)); - - minFee=feeInGwei; - maxFee=feeInGwei; - normalFee=feeInGwei; - etFee.setText(feeInGwei); - etFee.setError(null); - btnSend.setVisibility(View.VISIBLE); - feeRequestSuccess = true; - balanceRequestSuccess = true; - - dtVerifyed=new Date(); - minFeeInInternalUnits = mCard.InternalUnitsFromString(feeInGwei); - - } catch (JSONException e) { - e.printStackTrace(); - FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes"); - } - } - } else { - FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes"); - } - } catch (JSONException e) { - e.printStackTrace(); - FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes"); - } - } - } - } - - private class ConnectFeeTask extends Fee_Task { - public ConnectFeeTask(SharedData sharedData) { - super(sharedData); - } - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - for (Fee_Request request : requests) { - if (request.error == null) { - long minFeeRate = 0; - - try { - - try { - String tmpAnswer = request.getAsString(); - BigDecimal minFeeBD = new BigDecimal(tmpAnswer); - BigDecimal multiplicator = new BigDecimal("100000000"); - minFeeBD = minFeeBD.multiply(multiplicator); - BigInteger minFeeBI = minFeeBD.toBigInteger(); - minFeeRate = minFeeBI.longValue(); - } catch (Exception e) { - - if(sharedCounter != null) - { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - - - if(errCounter >= sharedCounter.allRequest) - { - progressBar.setVisibility(View.INVISIBLE); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - } - else - { - progressBar.setVisibility(View.INVISIBLE); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - - //FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - return; - } - - if (minFeeRate == 0) { - progressBar.setVisibility(View.INVISIBLE); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! Wrong data received from the node"); - return; - } - - long inputCount = request.txSize; - - if (inputCount != 0) { - minFeeRate = minFeeRate * inputCount; - } else { - minFeeRate = minFeeRate * 256; - } - } catch (Exception e) { - e.printStackTrace(); - if(sharedCounter != null) - { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if(errCounter >= sharedCounter.allRequest) - { - progressBar.setVisibility(View.INVISIBLE); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - } - else - { - progressBar.setVisibility(View.INVISIBLE); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - return; - } - - progressBar.setVisibility(View.INVISIBLE); - - float finalFee = (float) minFeeRate / (float) 10000; - - finalFee = Math.round(finalFee) / (float) 10000; - - if (request.getBlockCount() == Fee_Request.MINIMAL) { - minFee = String.valueOf(finalFee); - minFeeInInternalUnits = mCard.InternalUnitsFromString(String.valueOf(finalFee)); - } else if (request.getBlockCount() == Fee_Request.NORMAL) { - normalFee = String.valueOf(finalFee); - } else if (request.getBlockCount() == Fee_Request.PRIORITY) { - maxFee = String.valueOf(finalFee); - } - - doSetFee(rgFee.getCheckedRadioButtonId()); - - etFee.setError(null); - feeRequestSuccess = true; - if(feeRequestSuccess && balanceRequestSuccess) { - btnSend.setVisibility(View.VISIBLE); - } - dtVerifyed=new Date(); - - } else { - - if(sharedCounter != null) - { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if(errCounter >= sharedCounter.allRequest) - { - progressBar.setVisibility(View.INVISIBLE); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - } - else - { - progressBar.setVisibility(View.INVISIBLE); - FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); - } - } - } - } - } - - private void doSetFee(int checkedRadioButtonId) { - switch (checkedRadioButtonId) { - case R.id.rbMinimalFee: - if (minFee != null) etFee.setText(minFee); - else etFee.setText("?"); - break; - case R.id.rbNormalFee: - if (normalFee != null) etFee.setText(normalFee); - else etFee.setText("?"); - break; - case R.id.rbMaximumFee: - if (maxFee != null) etFee.setText(maxFee); - else etFee.setText("?"); - break; - } - } - - @Override - public void onResume() { - super.onResume(); - mNfcManager.onResume(); - } - - @Override - public void onPause() { - super.onPause(); - mNfcManager.onPause(); - } - - @Override - public void onStop() { - super.onStop(); - mNfcManager.onStop(); - } -} +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.app.AppCompatActivity; +import android.text.Editable; +import android.text.Html; +import android.text.Spanned; +import android.text.TextWatcher; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.RadioGroup; +import android.widget.TextView; +import android.widget.Toast; + +import com.tangem.cardReader.NfcManager; +import com.tangem.cardReader.Util; + +import org.json.JSONException; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback { + + private static final int REQUEST_CODE_SIGN_PAYMENT = 1; + private static final int REQUEST_CODE_REQUEST_PIN2 = 2; + Button btnSend; + boolean feeRequestSuccess = false; + boolean balanceRequestSuccess = false; + EditText etWallet; + TextView tvCardID, tvBalance, tvCurrency, tvCurrency2, tvBalanceEquivalent, tvAmountEquivalent, tvFeeEquivalent; + EditText etAmount; + EditText etFee; + ImageView ivCamera; + Tangem_Card mCard; + RadioGroup rgFee; + String minFee = null, maxFee = null, normalFee = null; + Long minFeeInInternalUnits = 0L; + private NfcManager mNfcManager; + int requestPIN2Count = 0; + ProgressBar progressBar; + boolean nodeCheck = false; + Date dtVerifyed=null; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_confirm_payment); + + MainActivity.commonInit(getApplicationContext()); + mNfcManager = new NfcManager(this, this); + + mCard = new Tangem_Card(getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); + + progressBar = findViewById(R.id.progressBar); + + btnSend = findViewById(R.id.btnSend); + etWallet = findViewById(R.id.etWallet); + tvCardID = findViewById(R.id.tvCardID); + tvBalance = findViewById(R.id.tvBalance); + tvCurrency = findViewById(R.id.tvCurrency); + tvCurrency2 = findViewById(R.id.tvCurrency2); + etAmount = findViewById(R.id.etAmount); + etFee = findViewById(R.id.etFee); + tvBalanceEquivalent = findViewById(R.id.tvBalanceEquivalent); + tvAmountEquivalent = findViewById(R.id.tvAmountEquivalent); + + tvFeeEquivalent = findViewById(R.id.tvFeeEquivalent); + ivCamera = findViewById(R.id.ivCamera); + + rgFee = findViewById(R.id.rgFee); + rgFee.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + doSetFee(checkedId); + } + }); + + etAmount.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + try { + + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + tvAmountEquivalent.setText(engine.GetAmountEqualentDescriptor(mCard, etAmount.getText().toString())); + if (!mCard.getAmountEquivalentDescriptionAvailable()) { + tvAmountEquivalent.setError("Service unavailable"); + } else { + tvAmountEquivalent.setError(null); + } + } catch (Exception e) { + e.printStackTrace(); + tvAmountEquivalent.setText(""); + } + } + + @Override + public void afterTextChanged(Editable s) { + + } + }); + + etFee.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + try { + + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + String eqFee = engine.EvaluteFeeEquivalent(mCard, etFee.getText().toString()); + tvFeeEquivalent.setText(eqFee); + + if (!mCard.getAmountEquivalentDescriptionAvailable()) { + tvFeeEquivalent.setError("Service unavailable"); + } else { + tvFeeEquivalent.setError(null); + } + } catch (Exception e) { + e.printStackTrace(); + tvFeeEquivalent.setText(""); + } + } + + @Override + public void afterTextChanged(Editable s) { + + } + }); + + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + //tvBalance.setText(engine.GetBalanceWithAlter(mCard)); + if (mCard.getBlockchain() == Blockchain.Token) { + Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard)); + tvBalance.setText(html); + } else { + tvBalance.setText(engine.GetBalanceWithAlter(mCard)); + } + etAmount.setText(getIntent().getStringExtra("Amount")); + tvCurrency.setText(engine.GetBalanceCurrency(mCard)); + tvCurrency2.setText(engine.GetFeeCurrency()); + + tvCardID.setText(mCard.getCIDDescription()); + + //tvBalanceEquivalent.setText(mCard.getBalanceEquivalentDescription()); + tvBalanceEquivalent.setText(engine.GetBalanceEquivalent(mCard)); + if (!mCard.getAmountEquivalentDescriptionAvailable()) { + tvBalanceEquivalent.setError("Service unavailable"); + } else { + tvBalanceEquivalent.setError(null); + } + + etWallet.setText(getIntent().getStringExtra("Wallet")); + + etFee.setText("?"); + + btnSend.setVisibility(View.INVISIBLE); + feeRequestSuccess = false; + balanceRequestSuccess = false; + btnSend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + Calendar calendar=Calendar.getInstance(); + calendar.add(Calendar.MINUTE,-1); + + if( dtVerifyed==null || dtVerifyed.before(calendar.getTime()) ) { + FinishActivityWithError(Activity.RESULT_CANCELED, "The obtained data is outdated! Try again"); + return; + } + + CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain()); + + if (engineCoin.IsNeedCheckNode() && !nodeCheck) { + Toast.makeText(getBaseContext(), "Cannot reach current active blockchain node. Try again", Toast.LENGTH_LONG).show(); + return; + } + String txFee = etFee.getText().toString(); + String txAmount = etAmount.getText().toString(); + + + if (!engineCoin.HasBalanceInfo(mCard)) { + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes"); + return; + } else if (!engineCoin.IsBalanceNotZero(mCard)) { + FinishActivityWithError(Activity.RESULT_CANCELED, "The wallet is empty"); + return; + } else if(!engineCoin.CheckUnspentTransaction(mCard)) { + //else if (mCard.getUnspentTransactions().size() == 0 && mCard.getBlockchain() != Blockchain.Ethereum) { + FinishActivityWithError(Activity.RESULT_CANCELED, "Please wait for confirmation of incoming transaction"); + return; + } + + if(!engineCoin.CheckAmountValie(mCard, txAmount, txFee, minFeeInInternalUnits)) + { + FinishActivityWithError(Activity.RESULT_CANCELED, "Fee exceeds payment amount. Enter correct value and repeat sending."); + return; + } + + requestPIN2Count = 0; + Intent intent = new Intent(getBaseContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2); + } + }); + + if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet || mCard.getBlockchain() == Blockchain.Token) { + ETHRequestTask task = new ETHRequestTask(mCard.getBlockchain()); + Infura_Request req = Infura_Request.GetGasPrise(mCard.getWallet()); + req.setID(67); + req.setBlockchain(mCard.getBlockchain()); + rgFee.setEnabled(false); + task.execute(req); + } else { + + rgFee.setEnabled(true); + + SharedData data = new SharedData(SharedData.COUNT_REQUEST); + + CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain()); + + for(int i =0 ; i < data.allRequest; ++i) { + + String nodeAddress = engineCoin.GetNextNode(mCard); + int nodePort = engineCoin.GetNextNodePort(mCard); + //ConnectTask connectTaskEx = new ConnectTask(Blockchain.getNextServiceHost(mCard), Blockchain.getNextServicePort(mCard), data); + ConnectTask connectTaskEx = new ConnectTask(nodeAddress, nodePort, data); + + //connectTaskEx.execute(Electrum_Request.CheckBalance(mCard.getWallet())); + connectTaskEx.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR,Electrum_Request.CheckBalance(mCard.getWallet())); + } + + String nodeAddress = engineCoin.GetNode(mCard); + int nodePort = engineCoin.GetNodePort(mCard); + ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort, data); + + //ConnectTask connectTask = new ConnectTask(Blockchain.getServiceHost(mCard), Blockchain.getServicePort(mCard)); + + connectTask.execute(/*Electrum_Request.CheckBalance(mCard.getWallet()), */Electrum_Request.GetFee(mCard.getWallet())); + + int calcSize = 256; + try { + calcSize = BuildSize(etWallet.getText().toString(), "0.00", etAmount.getText().toString()); + } catch (Exception ex) { + Log.e("Build Fee error", ex.getMessage()); + } + + SharedData sharedFee = new SharedData(SharedData.COUNT_REQUEST); + + progressBar.setVisibility(View.VISIBLE); + for(int i = 0; i < SharedData.COUNT_REQUEST; ++i) + { + + ConnectFeeTask feeTask = new ConnectFeeTask(sharedFee); + + feeTask.execute(Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.NORMAL), + Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.MINIMAL), + Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.PRIORITY)); + } + } + + } + + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + Intent intent = new Intent(); + intent.putExtra("message", "Operation canceled"); + setResult(Activity.RESULT_CANCELED, intent); + finish(); + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_CODE_SIGN_PAYMENT) { + if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { + Tangem_Card updatedCard=new Tangem_Card(data.getStringExtra("UID")); + updatedCard.LoadFromBundle(data.getBundleExtra("Card")); + mCard=updatedCard; + } + if (resultCode == SignPaymentActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) { + requestPIN2Count++; + Intent intent = new Intent(getBaseContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2); + return; + } + setResult(resultCode, data); + finish(); + } else if (requestCode == REQUEST_CODE_REQUEST_PIN2) { + if (resultCode == Activity.RESULT_OK) { + Intent intent = new Intent(getBaseContext(), SignPaymentActivity.class); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + intent.putExtra("Wallet", etWallet.getText().toString()); + intent.putExtra("Amount", etAmount.getText().toString()); + intent.putExtra("Fee", etFee.getText().toString()); + startActivityForResult(intent, REQUEST_CODE_SIGN_PAYMENT); + } else { + Toast.makeText(getBaseContext(), "PIN2 is required to sign the payment", Toast.LENGTH_LONG).show(); + } + } + } + + void FinishActivityWithError(int errorCode, String message) { + //Snackbar.make(etFee, message, Snackbar.LENGTH_LONG).show(); + Intent intent = new Intent(); + intent.putExtra("message", message); + setResult(errorCode, intent); + finish(); + } + + @Override + public void onTagDiscovered(Tag tag) { + try { + Log.w(getClass().getName(), "Ignore discovered tag!"); + mNfcManager.IgnoreTag(tag); + } catch (IOException e) { + e.printStackTrace(); + } + } + + int BuildSize(String outputAddress, String outFee, String outAmount) throws Exception { + String myAddress = mCard.getWallet(); + String changeAddress = myAddress; //"n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"; + byte[] pbKey = mCard.getWalletPublicKey(); + byte[] pbComprKey = mCard.getWalletPublicKeyRar(); + + // Build script for our address + List rawTxList = mCard.getUnspentTransactions(); + byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; + + // Collect unspent + ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + + long fullAmount = 0; + for (int i = 0; i < unspentOutputs.size(); ++i) { + fullAmount += unspentOutputs.get(i).value; + } + + // Get first unspent + UnspentOutputInfo outPut = unspentOutputs.get(0); + int outPutIndex = outPut.outputIndex; + + // get prev TX id; + String prevTXID = rawTxList.get(0).txID;//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2"; + + long fees = FormatUtil.ConvertStringToLong(outFee); + long amount = FormatUtil.ConvertStringToLong(outAmount); + amount = amount - fees; + + long change = fullAmount - fees - amount; + + if (amount + fees > fullAmount) { + throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount)); + } + + byte[][] hashesForSign = new byte[unspentOutputs.size()][]; + + for (int i = 0; i < unspentOutputs.size(); ++i) { + byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, changeAddress, unspentOutputs, i, amount, change); + + byte[] hashData = Util.calculateSHA256(newTX); + byte[] doubleHashData = Util.calculateSHA256(hashData); + + Log.e("TX_BODY_1", BTCUtils.toHex(newTX)); + Log.e("TX_HASH_1", BTCUtils.toHex(hashData)); + Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData)); + + unspentOutputs.get(i).bodyDoubleHash = doubleHashData; + unspentOutputs.get(i).bodyHash = hashData; + + hashesForSign[i] = doubleHashData; + } + + byte[] signFromCard = new byte[64 * unspentOutputs.size()]; + + for (int i = 0; i < unspentOutputs.size(); ++i) { + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); + byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey); + unspentOutputs.get(i).scriptForBuild = encodingSign; + } + + byte[] realTX = BTCUtils.buildTXForSend(outputAddress, changeAddress, unspentOutputs, amount, change); + + return realTX.length; + } + + private class ConnectTask extends Electrum_Task { + public ConnectTask(String host, int port) { + super(host, port); + } + + public ConnectTask(String host, int port, SharedData sharedData) { + super(host, port, sharedData); + } + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + for (Electrum_Request request : requests) { + try { + if (request.error == null) { + if (request.isMethod(Electrum_Request.METHOD_GetBalance)) { + try { + etFee.setText("--"); + + //String mWalletAddress = request.getParams().getString(0); + if ((request.getResult().getInt("confirmed") + request.getResult().getInt("unconfirmed")) / mCard.getBlockchain().getMultiplier() * 1000000.0 < Float.parseFloat(etAmount.getText().toString())) { + etFee.setError("Not enough funds"); + balanceRequestSuccess = false; + btnSend.setVisibility(View.INVISIBLE); + dtVerifyed=null; + nodeCheck = false; + } else { + etFee.setError(null); + balanceRequestSuccess = true; + if(feeRequestSuccess && balanceRequestSuccess) { + btnSend.setVisibility(View.VISIBLE); + + } + dtVerifyed=new Date(); + nodeCheck = true; + } + } catch (JSONException e) { + if(sharedCounter != null) + { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if(errCounter >= sharedCounter.allRequest) + { + e.printStackTrace(); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes"); + } + } + else + { + e.printStackTrace(); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes"); + } + } + } else if (request.isMethod(Electrum_Request.METHOD_GetFee)) { + if (request.getResultString() == "-1") { + etFee.setText("3"); + } + } + } else { +// etFee.setError(request.error); +// btnSend.setVisibility(View.INVISIBLE); + if(sharedCounter != null) + { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if(errCounter >= sharedCounter.allRequest) + { + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + } + else + { + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + return; + } + } catch (JSONException e) { + if(sharedCounter != null) + { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if(errCounter >= sharedCounter.allRequest) + { + e.printStackTrace(); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + + } + } + else + { + e.printStackTrace(); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + } + } + + } + } + + private class ETHRequestTask extends Infura_Task { + ETHRequestTask(Blockchain blockchain){ + super(blockchain); + } + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + for (Infura_Request request : requests) { + try { + Long price = 0L; + if (request.error == null) { + + if (request.isMethod(Infura_Request.METHOD_ETH_GetGasPrice)) { + try { + String gasPrice = request.getResultString(); + gasPrice = gasPrice.substring(2); + BigInteger l = new BigInteger(gasPrice, 16); + + BigInteger m = mCard.getBlockchain() == Blockchain.Token ? BigInteger.valueOf(55000) : BigInteger.valueOf(21000); + l = l.multiply(m); + String feeInGwei = mCard.getAmountInGwei(String.valueOf(l)); + + minFee=feeInGwei; + maxFee=feeInGwei; + normalFee=feeInGwei; + etFee.setText(feeInGwei); + etFee.setError(null); + btnSend.setVisibility(View.VISIBLE); + feeRequestSuccess = true; + balanceRequestSuccess = true; + + dtVerifyed=new Date(); + minFeeInInternalUnits = mCard.InternalUnitsFromString(feeInGwei); + + } catch (JSONException e) { + e.printStackTrace(); + FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes"); + } + } + } else { + FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes"); + } + } catch (JSONException e) { + e.printStackTrace(); + FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes"); + } + } + } + } + + private class ConnectFeeTask extends Fee_Task { + public ConnectFeeTask(SharedData sharedData) { + super(sharedData); + } + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + for (Fee_Request request : requests) { + if (request.error == null) { + long minFeeRate = 0; + + try { + + try { + String tmpAnswer = request.getAsString(); + BigDecimal minFeeBD = new BigDecimal(tmpAnswer); + BigDecimal multiplicator = new BigDecimal("100000000"); + minFeeBD = minFeeBD.multiply(multiplicator); + BigInteger minFeeBI = minFeeBD.toBigInteger(); + minFeeRate = minFeeBI.longValue(); + } catch (Exception e) { + + if(sharedCounter != null) + { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + + + if(errCounter >= sharedCounter.allRequest) + { + progressBar.setVisibility(View.INVISIBLE); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + } + else + { + progressBar.setVisibility(View.INVISIBLE); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + + //FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + return; + } + + if (minFeeRate == 0) { + progressBar.setVisibility(View.INVISIBLE); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! Wrong data received from the node"); + return; + } + + long inputCount = request.txSize; + + if (inputCount != 0) { + minFeeRate = minFeeRate * inputCount; + } else { + minFeeRate = minFeeRate * 256; + } + } catch (Exception e) { + e.printStackTrace(); + if(sharedCounter != null) + { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if(errCounter >= sharedCounter.allRequest) + { + progressBar.setVisibility(View.INVISIBLE); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + } + else + { + progressBar.setVisibility(View.INVISIBLE); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + return; + } + + progressBar.setVisibility(View.INVISIBLE); + + float finalFee = (float) minFeeRate / (float) 10000; + + finalFee = Math.round(finalFee) / (float) 10000; + + if (request.getBlockCount() == Fee_Request.MINIMAL) { + minFee = String.valueOf(finalFee); + minFeeInInternalUnits = mCard.InternalUnitsFromString(String.valueOf(finalFee)); + } else if (request.getBlockCount() == Fee_Request.NORMAL) { + normalFee = String.valueOf(finalFee); + } else if (request.getBlockCount() == Fee_Request.PRIORITY) { + maxFee = String.valueOf(finalFee); + } + + doSetFee(rgFee.getCheckedRadioButtonId()); + + etFee.setError(null); + feeRequestSuccess = true; + if(feeRequestSuccess && balanceRequestSuccess) { + btnSend.setVisibility(View.VISIBLE); + } + dtVerifyed=new Date(); + + } else { + + if(sharedCounter != null) + { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if(errCounter >= sharedCounter.allRequest) + { + progressBar.setVisibility(View.INVISIBLE); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + } + else + { + progressBar.setVisibility(View.INVISIBLE); + FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes"); + } + } + } + } + } + + private void doSetFee(int checkedRadioButtonId) { + switch (checkedRadioButtonId) { + case R.id.rbMinimalFee: + if (minFee != null) etFee.setText(minFee); + else etFee.setText("?"); + break; + case R.id.rbNormalFee: + if (normalFee != null) etFee.setText(normalFee); + else etFee.setText("?"); + break; + case R.id.rbMaximumFee: + if (maxFee != null) etFee.setText(maxFee); + else etFee.setText("?"); + break; + } + } + + @Override + public void onResume() { + super.onResume(); + mNfcManager.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mNfcManager.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + mNfcManager.onStop(); + } +} diff --git a/app/src/main/java/com/tangem/wallet/CreateNewWalletActivity.java b/app/src/main/java/com/tangem/wallet/CreateNewWalletActivity.java index 0c55114850..d9da77718f 100644 --- a/app/src/main/java/com/tangem/wallet/CreateNewWalletActivity.java +++ b/app/src/main/java/com/tangem/wallet/CreateNewWalletActivity.java @@ -1,330 +1,330 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.IsoDep; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.NfcManager; -import com.tangem.cardReader.Util; - -public class CreateNewWalletActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications { - - public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER; - private Tangem_Card mCard; - private TextView tvCardID; - private NfcManager mNfcManager; - private static final String logTag = "CreateNewActivity"; - private ProgressBar progressBar; - private CreateNewWalletTask createNewWalletTask; - private boolean lastReadSuccess = true; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_create_new_wallet); - - MainActivity.commonInit(getApplicationContext()); - mCard = new Tangem_Card(getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); - - tvCardID = (TextView) findViewById(R.id.tvCardID); - tvCardID.setText(mCard.getCIDDescription()); - - mNfcManager = new NfcManager(this, this); - - progressBar = (ProgressBar) findViewById(R.id.progressBar); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } - - @Override - public void onTagDiscovered(Tag tag) { - try { - // get IsoDep handle and run cardReader thread - final IsoDep isoDep = IsoDep.get(tag); - if (isoDep == null) { - throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); - } - byte UID[] = tag.getId(); - String sUID = Util.byteArrayToHexString(UID); - Log.v(logTag, "UID: " + sUID); - - if (sUID.equals(mCard.getUID())) { - if (lastReadSuccess) { - isoDep.setTimeout(mCard.getPauseBeforePIN2() + 5000); - } else { - isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000); - } - createNewWalletTask = new CreateNewWalletTask(isoDep, this); - createNewWalletTask.start(); - } else { - Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")"); - mNfcManager.IgnoreTag(isoDep.getTag()); - return; - } - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - @Override - public void onResume() { - super.onResume(); - mNfcManager.onResume(); - } - - @Override - public void onPause() { - mNfcManager.onPause(); - if (createNewWalletTask != null) { - createNewWalletTask.cancel(true); - } - super.onPause(); - } - - @Override - public void onStop() { - // dismiss enable NFC dialog - mNfcManager.onStop(); - if (createNewWalletTask != null) { - createNewWalletTask.cancel(true); - } - super.onStop(); - } - -// @Override -// public Dialog CreateNFCDialog(int id, AlertDialogWrapper.Builder builder, LayoutInflater li) { -// return mNfcManager.ShowNFCEnableDialog(); //onCreateDialog(id, builder, li); -// } - - private class CreateNewWalletTask extends Thread { - - IsoDep mIsoDep; - CardProtocol.Notifications mNotifications; - private boolean isCancelled = false; - - public CreateNewWalletTask(IsoDep isoDep, CardProtocol.Notifications notifications) { - mIsoDep = isoDep; - mNotifications = notifications; - } - - @Override - public void run() { - if (mIsoDep == null) { - return; - } - CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications); - mNotifications.OnReadStart(protocol); - try { - // for Samsung's bugs - - // Workaround for the Samsung Galaxy S5 (since the - // first connection always hangs on transceive). - int timeout = mIsoDep.getTimeout(); - mIsoDep.connect(); - mIsoDep.close(); - mIsoDep.connect(); - mIsoDep.setTimeout(timeout); - try { - mNotifications.OnReadProgress(protocol, 5); - - Log.i("CreateNewWalletTask", "[-- Start create new wallet --]"); - - if (isCancelled) return; - protocol.run_VerifyCard(); - - Log.i("CreateNewWalletTask", "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName()); - - mNotifications.OnReadProgress(protocol, 30); - if (isCancelled) return; - -// if (mCard.getPauseBeforePIN2() > 0) { -// mNotifications.OnReadWait(mCard.getPauseBeforePIN2()); -// } -// try { - protocol.run_CreateWallet(PINStorage.getPIN2()); -// } finally { -// mNotifications.OnReadWait(0); -// } - mNotifications.OnReadProgress(protocol, 60); - if (isCancelled) return; - - protocol.run_Read(); - - } finally { - mNfcManager.IgnoreTag(mIsoDep.getTag()); - } - } catch (Exception e) { - e.printStackTrace(); - protocol.setError(e); - - } finally { - Log.i("CreateNewWalletTask", "[-- Finish create new wallet --]"); - mNotifications.OnReadFinish(protocol); - } - } - - public void cancel(Boolean AllowInterrupt) { - try { - if (this.isAlive()) { - isCancelled = true; - join(500); - } - if (this.isAlive() && AllowInterrupt) { - interrupt(); - mNotifications.OnReadCancel(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - } - - public void OnReadStart(CardProtocol cardProtocol) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setVisibility(View.VISIBLE); - progressBar.setProgress(5); - } - }); - } - - public void OnReadFinish(final CardProtocol cardProtocol) { - - createNewWalletTask = null; - - if (cardProtocol != null) { - if (cardProtocol.getError() == null) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); - Intent intent = new Intent(); - intent.putExtra("UID", cardProtocol.getCard().getUID()); - intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); - setResult(Activity.RESULT_OK, intent); - finish(); - } - }); - } else { - lastReadSuccess = false; - if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - } - }); - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - Intent intent = new Intent(); - intent.putExtra("message", "Cannot create wallet. Make sure you enter correct PIN2!"); - intent.putExtra("UID", cardProtocol.getCard().getUID()); - intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); - setResult(RESULT_INVALID_PIN, intent); - finish(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - return; - } else { - progressBar.post(new Runnable() { - @Override - public void run() { - if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { - if (!NoExtendedLengthSupportDialog.allreadyShowed) { - new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog"); - } - } else { - Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show(); - } - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - } - }); - - } - } - } - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - public void OnReadProgress(CardProtocol protocol, final int progress) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(progress); - } - }); - } - - public void OnReadCancel() { - - createNewWalletTask = null; - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - @Override - public void OnReadWait(final int msec) { - WaitSecurityDelayDialog.OnReadWait(this, msec); - } - - @Override - public void OnReadBeforeRequest(int timeout) { - WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout); - } - - @Override - public void OnReadAfterRequest() { - WaitSecurityDelayDialog.onReadAfterRequest(this); - } -} - +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.NfcManager; +import com.tangem.cardReader.Util; + +public class CreateNewWalletActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications { + + public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER; + private Tangem_Card mCard; + private TextView tvCardID; + private NfcManager mNfcManager; + private static final String logTag = "CreateNewActivity"; + private ProgressBar progressBar; + private CreateNewWalletTask createNewWalletTask; + private boolean lastReadSuccess = true; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_create_new_wallet); + + MainActivity.commonInit(getApplicationContext()); + mCard = new Tangem_Card(getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); + + tvCardID = (TextView) findViewById(R.id.tvCardID); + tvCardID.setText(mCard.getCIDDescription()); + + mNfcManager = new NfcManager(this, this); + + progressBar = (ProgressBar) findViewById(R.id.progressBar); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } + + @Override + public void onTagDiscovered(Tag tag) { + try { + // get IsoDep handle and run cardReader thread + final IsoDep isoDep = IsoDep.get(tag); + if (isoDep == null) { + throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); + } + byte UID[] = tag.getId(); + String sUID = Util.byteArrayToHexString(UID); + Log.v(logTag, "UID: " + sUID); + + if (sUID.equals(mCard.getUID())) { + if (lastReadSuccess) { + isoDep.setTimeout(mCard.getPauseBeforePIN2() + 5000); + } else { + isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000); + } + createNewWalletTask = new CreateNewWalletTask(isoDep, this); + createNewWalletTask.start(); + } else { + Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")"); + mNfcManager.IgnoreTag(isoDep.getTag()); + return; + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public void onResume() { + super.onResume(); + mNfcManager.onResume(); + } + + @Override + public void onPause() { + mNfcManager.onPause(); + if (createNewWalletTask != null) { + createNewWalletTask.cancel(true); + } + super.onPause(); + } + + @Override + public void onStop() { + // dismiss enable NFC dialog + mNfcManager.onStop(); + if (createNewWalletTask != null) { + createNewWalletTask.cancel(true); + } + super.onStop(); + } + +// @Override +// public Dialog CreateNFCDialog(int id, AlertDialogWrapper.Builder builder, LayoutInflater li) { +// return mNfcManager.ShowNFCEnableDialog(); //onCreateDialog(id, builder, li); +// } + + private class CreateNewWalletTask extends Thread { + + IsoDep mIsoDep; + CardProtocol.Notifications mNotifications; + private boolean isCancelled = false; + + public CreateNewWalletTask(IsoDep isoDep, CardProtocol.Notifications notifications) { + mIsoDep = isoDep; + mNotifications = notifications; + } + + @Override + public void run() { + if (mIsoDep == null) { + return; + } + CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications); + mNotifications.OnReadStart(protocol); + try { + // for Samsung's bugs - + // Workaround for the Samsung Galaxy S5 (since the + // first connection always hangs on transceive). + int timeout = mIsoDep.getTimeout(); + mIsoDep.connect(); + mIsoDep.close(); + mIsoDep.connect(); + mIsoDep.setTimeout(timeout); + try { + mNotifications.OnReadProgress(protocol, 5); + + Log.i("CreateNewWalletTask", "[-- Start create new wallet --]"); + + if (isCancelled) return; + protocol.run_VerifyCard(); + + Log.i("CreateNewWalletTask", "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName()); + + mNotifications.OnReadProgress(protocol, 30); + if (isCancelled) return; + +// if (mCard.getPauseBeforePIN2() > 0) { +// mNotifications.OnReadWait(mCard.getPauseBeforePIN2()); +// } +// try { + protocol.run_CreateWallet(PINStorage.getPIN2()); +// } finally { +// mNotifications.OnReadWait(0); +// } + mNotifications.OnReadProgress(protocol, 60); + if (isCancelled) return; + + protocol.run_Read(); + + } finally { + mNfcManager.IgnoreTag(mIsoDep.getTag()); + } + } catch (Exception e) { + e.printStackTrace(); + protocol.setError(e); + + } finally { + Log.i("CreateNewWalletTask", "[-- Finish create new wallet --]"); + mNotifications.OnReadFinish(protocol); + } + } + + public void cancel(Boolean AllowInterrupt) { + try { + if (this.isAlive()) { + isCancelled = true; + join(500); + } + if (this.isAlive() && AllowInterrupt) { + interrupt(); + mNotifications.OnReadCancel(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + public void OnReadStart(CardProtocol cardProtocol) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setVisibility(View.VISIBLE); + progressBar.setProgress(5); + } + }); + } + + public void OnReadFinish(final CardProtocol cardProtocol) { + + createNewWalletTask = null; + + if (cardProtocol != null) { + if (cardProtocol.getError() == null) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); + Intent intent = new Intent(); + intent.putExtra("UID", cardProtocol.getCard().getUID()); + intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); + setResult(Activity.RESULT_OK, intent); + finish(); + } + }); + } else { + lastReadSuccess = false; + if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + } + }); + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + Intent intent = new Intent(); + intent.putExtra("message", "Cannot create wallet. Make sure you enter correct PIN2!"); + intent.putExtra("UID", cardProtocol.getCard().getUID()); + intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); + setResult(RESULT_INVALID_PIN, intent); + finish(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + return; + } else { + progressBar.post(new Runnable() { + @Override + public void run() { + if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { + if (!NoExtendedLengthSupportDialog.allreadyShowed) { + new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog"); + } + } else { + Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show(); + } + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + } + }); + + } + } + } + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + public void OnReadProgress(CardProtocol protocol, final int progress) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(progress); + } + }); + } + + public void OnReadCancel() { + + createNewWalletTask = null; + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + @Override + public void OnReadWait(final int msec) { + WaitSecurityDelayDialog.OnReadWait(this, msec); + } + + @Override + public void OnReadBeforeRequest(int timeout) { + WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout); + } + + @Override + public void OnReadAfterRequest() { + WaitSecurityDelayDialog.onReadAfterRequest(this); + } +} + diff --git a/app/src/main/java/com/tangem/wallet/CryptoUtil.java b/app/src/main/java/com/tangem/wallet/CryptoUtil.java index 689d0d7eec..6500a6f7ed 100644 --- a/app/src/main/java/com/tangem/wallet/CryptoUtil.java +++ b/app/src/main/java/com/tangem/wallet/CryptoUtil.java @@ -1,270 +1,270 @@ -package com.tangem.wallet; - -import android.util.Log; - -import com.tangem.cardReader.Util; - -import org.spongycastle.asn1.ASN1EncodableVector; -import org.spongycastle.asn1.ASN1Integer; -import org.spongycastle.asn1.DERSequence; -import org.spongycastle.asn1.sec.SECNamedCurves; -import org.spongycastle.asn1.x9.X9ECParameters; -import org.spongycastle.asn1.x9.X9IntegerConverter; -import org.spongycastle.crypto.params.ECDomainParameters; -import org.spongycastle.crypto.params.ECPrivateKeyParameters; -import org.spongycastle.crypto.params.ECPublicKeyParameters; -import org.spongycastle.crypto.signers.ECDSASigner; -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; -import org.spongycastle.jce.spec.ECPublicKeySpec; -import org.spongycastle.math.ec.ECAlgorithms; -import org.spongycastle.math.ec.ECCurve; -import org.spongycastle.math.ec.ECPoint; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigInteger; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.PublicKey; -import java.security.Signature; -import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; - -import static org.bitcoinj.core.ECKey.CURVE; -import static org.bitcoinj.core.ECKey.HALF_CURVE_ORDER; - -/** - * Created by Ilia on 15.02.2018. - */ - -public class CryptoUtil { - - public static boolean checkHashSign2(byte[] pub, byte[] hash, BigInteger r, BigInteger s) - { - ECDSASigner signer = new ECDSASigner(); - - ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub), CURVE); - signer.init(false, params); - return signer.verifySignature(hash, r, s); - } - - public static boolean isCanonical(BigInteger s) { - return s.compareTo(HALF_CURVE_ORDER) <= 0; - } - - public static BigInteger toCanonicalised(BigInteger s) { - - // The order of the curve is the number of valid points that exist on that curve. If S is in the upper - // half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that - // N = 10 - // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. - // 10 - 8 == 2, giving us always the latter solution, which is canonical. - if(!isCanonical(s)) { - BigInteger canon = CURVE.getN().subtract(s); - Log.e("TX_SIGN", "non Canonical S"); - return canon; - } - - return s; - - } - - private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { - X9IntegerConverter x9 = new X9IntegerConverter(); - byte[] compEnc = x9.integerToBytes(xBN, 1 + x9.getByteLength(CURVE.getCurve())); - compEnc[0] = (byte) (yBit ? 0x03 : 0x02); - return CURVE.getCurve().decodePoint(compEnc); - } - - public static byte[] recoverPubBytesFromSignature(int recId, ECDSASignature_ETH sig, byte[] messageHash) { - // 1.0 For j from 0 to h (h == recId here and the loop is outside this function) - // 1.1 Let x = r + jn - - X9ECParameters params = SECNamedCurves.getByName("secp256k1"); - ECDomainParameters CURVE2 = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); - - BigInteger n = CURVE2.getN(); // Curve order. - BigInteger i = BigInteger.valueOf((long) recId / 2); - BigInteger x = sig.r.add(i.multiply(n)); - // 1.2. Convert the integer x to an octet string X of length mlen using the conversion routine - // specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or mlen = ⌈m/8⌉. - // 1.3. Convert the octet string (16 set binary digits)||X to an elliptic curve point R using the - // conversion routine specified in Section 2.3.4. If this conversion routine outputs “invalid”, then - // do another iteration of Step 1. - // - // More concisely, what these points mean is to use X as a compressed public key. - ECCurve.Fp curve = (ECCurve.Fp) CURVE2.getCurve(); - BigInteger prime = curve.getQ(); // Bouncy Castle is not consistent about the letter it uses for the prime. - if (x.compareTo(prime) >= 0) { - // Cannot have point co-ordinates larger than this as everything takes place modulo Q. - return null; - } - // Compressed keys require you to know an extra bit of data about the y-coord as there are two possibilities. - // So it's encoded in the recId. - ECPoint R = decompressKey(x, (recId & 1) == 1); - // 1.4. If nR != point at infinity, then do another iteration of Step 1 (callers responsibility). - if (!R.multiply(n).isInfinity()) - return null; - // 1.5. Compute e from M using Steps 2 and 3 of ECDSA signature verification. - BigInteger e = new BigInteger(1, messageHash); - // 1.6. For k from 1 to 2 do the following. (loop is outside this function via iterating recId) - // 1.6.1. Compute a candidate public key as: - // Q = mi(r) * (sR - eG) - // - // Where mi(x) is the modular multiplicative inverse. We transform this into the following: - // Q = (mi(r) * s ** R) + (mi(r) * -e ** G) - // Where -e is the modular additive inverse of e, that is z such that z + e = 0 (mod n). In the above equation - // ** is point multiplication and + is point addition (the EC group operator). - // - // We can find the additive inverse by subtracting e from zero then taking the mod. For example the additive - // inverse of 3 modulo 11 is 8 because 3 + 8 mod 11 = 0, and -3 mod 11 = 8. - BigInteger eInv = BigInteger.ZERO.subtract(e).mod(n); - BigInteger rInv = sig.r.modInverse(n); - BigInteger srInv = rInv.multiply(sig.s).mod(n); - BigInteger eInvrInv = rInv.multiply(eInv).mod(n); - ECPoint.Fp q = (ECPoint.Fp) ECAlgorithms.sumOfTwoMultiplies(CURVE2.getG(), eInvrInv, R, srInv); - return q.getEncoded(/* compressed */ false); - } - - public static byte[] calcSign(byte[] priv, byte[] hash) - { - ECDSASigner signer = new ECDSASigner(); - BigInteger d = new BigInteger(priv); - ECPrivateKeyParameters params = new ECPrivateKeyParameters(d, CURVE); - signer.init(true, params); - BigInteger[] rs = signer.generateSignature(hash); - byte[] r = rs[0].toByteArray(); - byte[] s = rs[1].toByteArray(); - byte[] sign = new byte[64]; - for(int i = 0; i < 32; ++i) - { - sign[i] = r[i]; - sign[i+32] = s[i]; - } - return sign; - - } - - public static boolean isEncodingCanonical(byte[] signature) { - // See Bitcoin Core's IsCanonicalSignature, https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 - // A canonical signature exists of: <30> <02> <02> - // Where R and S are not negative (their first byte has its highest bit not set), and not - // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, - // in which case a single 0 byte is necessary and even required). - if (signature.length < 9 || signature.length > 73) - return false; - - int hashType = (signature[signature.length-1] & 0xff) & ~0x80; // mask the byte to prevent sign-extension hurting us - if (hashType < 1 || hashType > 3) - return false; - - // "wrong type" "wrong length marker" - if ((signature[0] & 0xff) != 0x30 || (signature[1] & 0xff) != signature.length-3) - return false; - - int lenR = signature[3] & 0xff; - if (5 + lenR >= signature.length || lenR == 0) - return false; - int lenS = signature[5+lenR] & 0xff; - if (lenR + lenS + 7 != signature.length || lenS == 0) - return false; - - // R value type mismatch R value negative - if (signature[4-2] != 0x02 || (signature[4] & 0x80) == 0x80) - return false; - if (lenR > 1 && signature[4] == 0x00 && (signature[4+1] & 0x80) != 0x80) - return false; // R value excessively padded - - // S value type mismatch S value negative - if (signature[6 + lenR - 2] != 0x02 || (signature[6 + lenR] & 0x80) == 0x80) - return false; - if (lenS > 1 && signature[6 + lenR] == 0x00 && (signature[6 + lenR + 1] & 0x80) != 0x80) - return false; // S value excessively padded - - return true; - } - - - public static boolean VerifySign(byte[] tlvPublicKey, byte[] data, byte[] tlvSignature) throws IOException, SignatureException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException { - Signature signature = Signature.getInstance("SHA256withECDSA"); - ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); - KeyFactory factory = KeyFactory.getInstance("EC", "SC"); - - ECPoint p1 = spec.getCurve().decodePoint(tlvPublicKey); - ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec); - - PublicKey publicKey = factory.generatePublic(keySpec); - signature.initVerify(publicKey); - signature.update(data); - - ASN1EncodableVector v = new ASN1EncodableVector(); - int size = tlvSignature.length / 2; - v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(tlvSignature, 0, size)))); - v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(tlvSignature, size, size * 2)))); - byte[] sigDer = new DERSequence(v).getEncoded(); - - return signature.verify(sigDer); - } - - private static byte[] leaderZero(byte[] s) - { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - if (s[0] > 0x7f) { - baos.write((byte) 0x00); - } - for(int i = 0; i < 32; ++i) - baos.write(s[i]); - - return baos.toByteArray(); - } - public static boolean checkHashSign(byte[] pub, byte[] hash, byte[] sign) - { - byte[] rtmp = new byte[32]; - byte[] stmp = new byte[32]; - - for(int i = 0; i< 32; ++i) - { - rtmp[i] = sign[i]; - stmp[i] = sign[i+32]; - } - - leaderZero(rtmp); - byte[] r2 = leaderZero(rtmp); - byte[] s2 = leaderZero(stmp); - - BigInteger r = new BigInteger(r2); - BigInteger s = new BigInteger(s2); - ECDSASigner signer = new ECDSASigner(); - - ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub), CURVE); - signer.init(false, params); - return signer.verifySignature(hash, r, s); - } - public static byte[] doubleSha256(byte[] bytes) { - try { - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - return sha256.digest(sha256.digest(bytes)); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - public static byte[] sha256ripemd160(byte[] publicKey) { - try { - MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); - byte[] sha256hash = sha256.digest(publicKey); - byte[] hashedPublicKey = Util.calculateRIPEMD160(sha256hash); - return hashedPublicKey; - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - } - return null; - } -} +package com.tangem.wallet; + +import android.util.Log; + +import com.tangem.cardReader.Util; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.asn1.sec.SECNamedCurves; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.asn1.x9.X9IntegerConverter; +import org.spongycastle.crypto.params.ECDomainParameters; +import org.spongycastle.crypto.params.ECPrivateKeyParameters; +import org.spongycastle.crypto.params.ECPublicKeyParameters; +import org.spongycastle.crypto.signers.ECDSASigner; +import org.spongycastle.jce.ECNamedCurveTable; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; +import org.spongycastle.math.ec.ECAlgorithms; +import org.spongycastle.math.ec.ECCurve; +import org.spongycastle.math.ec.ECPoint; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; + +import static org.bitcoinj.core.ECKey.CURVE; +import static org.bitcoinj.core.ECKey.HALF_CURVE_ORDER; + +/** + * Created by Ilia on 15.02.2018. + */ + +public class CryptoUtil { + + public static boolean checkHashSign2(byte[] pub, byte[] hash, BigInteger r, BigInteger s) + { + ECDSASigner signer = new ECDSASigner(); + + ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub), CURVE); + signer.init(false, params); + return signer.verifySignature(hash, r, s); + } + + public static boolean isCanonical(BigInteger s) { + return s.compareTo(HALF_CURVE_ORDER) <= 0; + } + + public static BigInteger toCanonicalised(BigInteger s) { + + // The order of the curve is the number of valid points that exist on that curve. If S is in the upper + // half of the number of valid points, then bring it back to the lower half. Otherwise, imagine that + // N = 10 + // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) are valid solutions. + // 10 - 8 == 2, giving us always the latter solution, which is canonical. + if(!isCanonical(s)) { + BigInteger canon = CURVE.getN().subtract(s); + Log.e("TX_SIGN", "non Canonical S"); + return canon; + } + + return s; + + } + + private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { + X9IntegerConverter x9 = new X9IntegerConverter(); + byte[] compEnc = x9.integerToBytes(xBN, 1 + x9.getByteLength(CURVE.getCurve())); + compEnc[0] = (byte) (yBit ? 0x03 : 0x02); + return CURVE.getCurve().decodePoint(compEnc); + } + + public static byte[] recoverPubBytesFromSignature(int recId, ECDSASignature_ETH sig, byte[] messageHash) { + // 1.0 For j from 0 to h (h == recId here and the loop is outside this function) + // 1.1 Let x = r + jn + + X9ECParameters params = SECNamedCurves.getByName("secp256k1"); + ECDomainParameters CURVE2 = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + + BigInteger n = CURVE2.getN(); // Curve order. + BigInteger i = BigInteger.valueOf((long) recId / 2); + BigInteger x = sig.r.add(i.multiply(n)); + // 1.2. Convert the integer x to an octet string X of length mlen using the conversion routine + // specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or mlen = ⌈m/8⌉. + // 1.3. Convert the octet string (16 set binary digits)||X to an elliptic curve point R using the + // conversion routine specified in Section 2.3.4. If this conversion routine outputs “invalid”, then + // do another iteration of Step 1. + // + // More concisely, what these points mean is to use X as a compressed public key. + ECCurve.Fp curve = (ECCurve.Fp) CURVE2.getCurve(); + BigInteger prime = curve.getQ(); // Bouncy Castle is not consistent about the letter it uses for the prime. + if (x.compareTo(prime) >= 0) { + // Cannot have point co-ordinates larger than this as everything takes place modulo Q. + return null; + } + // Compressed keys require you to know an extra bit of data about the y-coord as there are two possibilities. + // So it's encoded in the recId. + ECPoint R = decompressKey(x, (recId & 1) == 1); + // 1.4. If nR != point at infinity, then do another iteration of Step 1 (callers responsibility). + if (!R.multiply(n).isInfinity()) + return null; + // 1.5. Compute e from M using Steps 2 and 3 of ECDSA signature verification. + BigInteger e = new BigInteger(1, messageHash); + // 1.6. For k from 1 to 2 do the following. (loop is outside this function via iterating recId) + // 1.6.1. Compute a candidate public key as: + // Q = mi(r) * (sR - eG) + // + // Where mi(x) is the modular multiplicative inverse. We transform this into the following: + // Q = (mi(r) * s ** R) + (mi(r) * -e ** G) + // Where -e is the modular additive inverse of e, that is z such that z + e = 0 (mod n). In the above equation + // ** is point multiplication and + is point addition (the EC group operator). + // + // We can find the additive inverse by subtracting e from zero then taking the mod. For example the additive + // inverse of 3 modulo 11 is 8 because 3 + 8 mod 11 = 0, and -3 mod 11 = 8. + BigInteger eInv = BigInteger.ZERO.subtract(e).mod(n); + BigInteger rInv = sig.r.modInverse(n); + BigInteger srInv = rInv.multiply(sig.s).mod(n); + BigInteger eInvrInv = rInv.multiply(eInv).mod(n); + ECPoint.Fp q = (ECPoint.Fp) ECAlgorithms.sumOfTwoMultiplies(CURVE2.getG(), eInvrInv, R, srInv); + return q.getEncoded(/* compressed */ false); + } + + public static byte[] calcSign(byte[] priv, byte[] hash) + { + ECDSASigner signer = new ECDSASigner(); + BigInteger d = new BigInteger(priv); + ECPrivateKeyParameters params = new ECPrivateKeyParameters(d, CURVE); + signer.init(true, params); + BigInteger[] rs = signer.generateSignature(hash); + byte[] r = rs[0].toByteArray(); + byte[] s = rs[1].toByteArray(); + byte[] sign = new byte[64]; + for(int i = 0; i < 32; ++i) + { + sign[i] = r[i]; + sign[i+32] = s[i]; + } + return sign; + + } + + public static boolean isEncodingCanonical(byte[] signature) { + // See Bitcoin Core's IsCanonicalSignature, https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 + // A canonical signature exists of: <30> <02> <02> + // Where R and S are not negative (their first byte has its highest bit not set), and not + // excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, + // in which case a single 0 byte is necessary and even required). + if (signature.length < 9 || signature.length > 73) + return false; + + int hashType = (signature[signature.length-1] & 0xff) & ~0x80; // mask the byte to prevent sign-extension hurting us + if (hashType < 1 || hashType > 3) + return false; + + // "wrong type" "wrong length marker" + if ((signature[0] & 0xff) != 0x30 || (signature[1] & 0xff) != signature.length-3) + return false; + + int lenR = signature[3] & 0xff; + if (5 + lenR >= signature.length || lenR == 0) + return false; + int lenS = signature[5+lenR] & 0xff; + if (lenR + lenS + 7 != signature.length || lenS == 0) + return false; + + // R value type mismatch R value negative + if (signature[4-2] != 0x02 || (signature[4] & 0x80) == 0x80) + return false; + if (lenR > 1 && signature[4] == 0x00 && (signature[4+1] & 0x80) != 0x80) + return false; // R value excessively padded + + // S value type mismatch S value negative + if (signature[6 + lenR - 2] != 0x02 || (signature[6 + lenR] & 0x80) == 0x80) + return false; + if (lenS > 1 && signature[6 + lenR] == 0x00 && (signature[6 + lenR + 1] & 0x80) != 0x80) + return false; // S value excessively padded + + return true; + } + + + public static boolean VerifySign(byte[] tlvPublicKey, byte[] data, byte[] tlvSignature) throws IOException, SignatureException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException { + Signature signature = Signature.getInstance("SHA256withECDSA"); + ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); + KeyFactory factory = KeyFactory.getInstance("EC", "SC"); + + ECPoint p1 = spec.getCurve().decodePoint(tlvPublicKey); + ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec); + + PublicKey publicKey = factory.generatePublic(keySpec); + signature.initVerify(publicKey); + signature.update(data); + + ASN1EncodableVector v = new ASN1EncodableVector(); + int size = tlvSignature.length / 2; + v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(tlvSignature, 0, size)))); + v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(tlvSignature, size, size * 2)))); + byte[] sigDer = new DERSequence(v).getEncoded(); + + return signature.verify(sigDer); + } + + private static byte[] leaderZero(byte[] s) + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if (s[0] > 0x7f) { + baos.write((byte) 0x00); + } + for(int i = 0; i < 32; ++i) + baos.write(s[i]); + + return baos.toByteArray(); + } + public static boolean checkHashSign(byte[] pub, byte[] hash, byte[] sign) + { + byte[] rtmp = new byte[32]; + byte[] stmp = new byte[32]; + + for(int i = 0; i< 32; ++i) + { + rtmp[i] = sign[i]; + stmp[i] = sign[i+32]; + } + + leaderZero(rtmp); + byte[] r2 = leaderZero(rtmp); + byte[] s2 = leaderZero(stmp); + + BigInteger r = new BigInteger(r2); + BigInteger s = new BigInteger(s2); + ECDSASigner signer = new ECDSASigner(); + + ECPublicKeyParameters params = new ECPublicKeyParameters(CURVE.getCurve().decodePoint(pub), CURVE); + signer.init(false, params); + return signer.verifySignature(hash, r, s); + } + public static byte[] doubleSha256(byte[] bytes) { + try { + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + return sha256.digest(sha256.digest(bytes)); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + public static byte[] sha256ripemd160(byte[] publicKey) { + try { + MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); + byte[] sha256hash = sha256.digest(publicKey); + byte[] hashedPublicKey = Util.calculateRIPEMD160(sha256hash); + return hashedPublicKey; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (NoSuchProviderException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/app/src/main/java/com/tangem/wallet/DerEncodingUtil.java b/app/src/main/java/com/tangem/wallet/DerEncodingUtil.java index 8d0afcfb60..7584345701 100644 --- a/app/src/main/java/com/tangem/wallet/DerEncodingUtil.java +++ b/app/src/main/java/com/tangem/wallet/DerEncodingUtil.java @@ -1,130 +1,130 @@ -package com.tangem.wallet; - -import org.spongycastle.asn1.ASN1Integer; -import org.spongycastle.asn1.DERSequenceGenerator; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigInteger; - -/** - * Created by Ilia on 15.02.2018. - */ - -public class DerEncodingUtil { - - public static byte[] PackInteger(byte[] s) - { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - baos.write((byte)0x02); - - byte length = (byte)s.length; - if (s[0] > 0x7f) { - baos.write((byte)(length+1)); - baos.write((byte) 0x00); - } - else { - baos.write((byte)length); - } - - for(int i = 0; i < length; ++i) - baos.write(s[i]); - - return baos.toByteArray(); - } - - public static byte[] packSignDer(BigInteger r, BigInteger s, byte[] pubKey) throws IOException - { - byte[] signDer = DerEncoding(r, s); - BitcoinOutputStream packKey = new BitcoinOutputStream(); - - packKey.write((byte)0x41); - packKey.write(pubKey); - - byte[] keyArray = packKey.toByteArray(); - - BitcoinOutputStream result = new BitcoinOutputStream(); - result.write((byte)(signDer.length+1)); - result.write(signDer); - result.write((byte)0x1); - - result.write(keyArray); - - return result.toByteArray(); - - } - - public static byte[] packSignDerBitcoinCash(BigInteger r, BigInteger s, byte[] pubKey) throws IOException - { - byte[] signDer = DerEncoding(r, s); - BitcoinOutputStream packKey = new BitcoinOutputStream(); - - packKey.write((byte)0x21); //compress key - packKey.write(pubKey); - - byte[] keyArray = packKey.toByteArray(); - - BitcoinOutputStream result = new BitcoinOutputStream(); - result.write((byte)(signDer.length+1)); - result.write(signDer); - result.write((byte)0x41); - - result.write(keyArray); - - return result.toByteArray(); - - } - - - public static byte[] DerEncoding(BigInteger r, BigInteger s) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(72); - DERSequenceGenerator seq = new DERSequenceGenerator(bos); - seq.addObject(new ASN1Integer(r)); - seq.addObject(new ASN1Integer(s)); - seq.close(); - return bos.toByteArray(); - } - - public static byte[] DerEncoding(byte[] sign) - { - byte[] r = sign; - byte[] s = new byte[32]; - for(int i =0; i < 32; ++i) - { - s[i] = sign[i+32]; - } - - byte[] newR = PackInteger(r); - byte[] newS = PackInteger(s); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - baos.write((byte)(newR.length+newS.length+2)); - baos.write((byte)newR.length); - baos.write(newR, 0, newR.length); - baos.write((byte)newS.length); - baos.write(newS, 0, newS.length); - - return baos.toByteArray(); - } - - public static byte[] DerEncodingBI(BigInteger[] sign) - { - byte[] r = sign[0].toByteArray(); - byte[] s = sign[1].toByteArray(); - - byte[] newR = PackInteger(r); - byte[] newS = PackInteger(s); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - baos.write((byte)(newR.length+newS.length+2)); - - baos.write((byte)newR.length); - baos.write(newR, 0, newR.length); - - baos.write((byte)newS.length); - baos.write(newS, 0, newS.length); - - return baos.toByteArray(); - } -} +package com.tangem.wallet; + +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequenceGenerator; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; + +/** + * Created by Ilia on 15.02.2018. + */ + +public class DerEncodingUtil { + + public static byte[] PackInteger(byte[] s) + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write((byte)0x02); + + byte length = (byte)s.length; + if (s[0] > 0x7f) { + baos.write((byte)(length+1)); + baos.write((byte) 0x00); + } + else { + baos.write((byte)length); + } + + for(int i = 0; i < length; ++i) + baos.write(s[i]); + + return baos.toByteArray(); + } + + public static byte[] packSignDer(BigInteger r, BigInteger s, byte[] pubKey) throws IOException + { + byte[] signDer = DerEncoding(r, s); + BitcoinOutputStream packKey = new BitcoinOutputStream(); + + packKey.write((byte)0x41); + packKey.write(pubKey); + + byte[] keyArray = packKey.toByteArray(); + + BitcoinOutputStream result = new BitcoinOutputStream(); + result.write((byte)(signDer.length+1)); + result.write(signDer); + result.write((byte)0x1); + + result.write(keyArray); + + return result.toByteArray(); + + } + + public static byte[] packSignDerBitcoinCash(BigInteger r, BigInteger s, byte[] pubKey) throws IOException + { + byte[] signDer = DerEncoding(r, s); + BitcoinOutputStream packKey = new BitcoinOutputStream(); + + packKey.write((byte)0x21); //compress key + packKey.write(pubKey); + + byte[] keyArray = packKey.toByteArray(); + + BitcoinOutputStream result = new BitcoinOutputStream(); + result.write((byte)(signDer.length+1)); + result.write(signDer); + result.write((byte)0x41); + + result.write(keyArray); + + return result.toByteArray(); + + } + + + public static byte[] DerEncoding(BigInteger r, BigInteger s) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(72); + DERSequenceGenerator seq = new DERSequenceGenerator(bos); + seq.addObject(new ASN1Integer(r)); + seq.addObject(new ASN1Integer(s)); + seq.close(); + return bos.toByteArray(); + } + + public static byte[] DerEncoding(byte[] sign) + { + byte[] r = sign; + byte[] s = new byte[32]; + for(int i =0; i < 32; ++i) + { + s[i] = sign[i+32]; + } + + byte[] newR = PackInteger(r); + byte[] newS = PackInteger(s); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + baos.write((byte)(newR.length+newS.length+2)); + baos.write((byte)newR.length); + baos.write(newR, 0, newR.length); + baos.write((byte)newS.length); + baos.write(newS, 0, newS.length); + + return baos.toByteArray(); + } + + public static byte[] DerEncodingBI(BigInteger[] sign) + { + byte[] r = sign[0].toByteArray(); + byte[] s = sign[1].toByteArray(); + + byte[] newR = PackInteger(r); + byte[] newS = PackInteger(s); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + baos.write((byte)(newR.length+newS.length+2)); + + baos.write((byte)newR.length); + baos.write(newR, 0, newR.length); + + baos.write((byte)newS.length); + baos.write(newS, 0, newS.length); + + return baos.toByteArray(); + } +} diff --git a/app/src/main/java/com/tangem/wallet/DeviceNFCAntennaLocation.java b/app/src/main/java/com/tangem/wallet/DeviceNFCAntennaLocation.java index f9279eb23f..c7d5070437 100644 --- a/app/src/main/java/com/tangem/wallet/DeviceNFCAntennaLocation.java +++ b/app/src/main/java/com/tangem/wallet/DeviceNFCAntennaLocation.java @@ -1,25 +1,25 @@ -package com.tangem.wallet; - -import android.os.Build; - -public class DeviceNFCAntennaLocation { - - public float X; - public float Y; - public boolean OnBackSide; - public float Strength; - - public void getAntennaLocation() { - String device = DeviceName.getDeviceName(); - String model = Build.DEVICE; - this.X = 0.5f; - this.Y = 0.33f; - this.OnBackSide = true; - this.Strength = 1.0f; - // Samsung -// if (model.contains("Samsung")) {this.Y = 0.33; this.Strength = 0.5; } -// if (model.contains("Sony")) {this.Y = 0.33; this.Strength = 0.5; } -// if (device == "Galaxy J5") {this.Y = 0.4; this.Strength = 0.5; } - if (device == "P10 lite") {this.Y = 0.03f; this.Strength = 0.8f; } - } -} +package com.tangem.wallet; + +import android.os.Build; + +public class DeviceNFCAntennaLocation { + + public float X; + public float Y; + public boolean OnBackSide; + public float Strength; + + public void getAntennaLocation() { + String device = DeviceName.getDeviceName(); + String model = Build.DEVICE; + this.X = 0.5f; + this.Y = 0.33f; + this.OnBackSide = true; + this.Strength = 1.0f; + // Samsung +// if (model.contains("Samsung")) {this.Y = 0.33; this.Strength = 0.5; } +// if (model.contains("Sony")) {this.Y = 0.33; this.Strength = 0.5; } +// if (device == "Galaxy J5") {this.Y = 0.4; this.Strength = 0.5; } + if (device == "P10 lite") {this.Y = 0.03f; this.Strength = 0.8f; } + } +} diff --git a/app/src/main/java/com/tangem/wallet/DeviceName.java b/app/src/main/java/com/tangem/wallet/DeviceName.java index ef1b1401b1..15bf9c1456 100644 --- a/app/src/main/java/com/tangem/wallet/DeviceName.java +++ b/app/src/main/java/com/tangem/wallet/DeviceName.java @@ -1,2091 +1,2091 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 20.04.2018. - */ -/* - * Copyright (C) 2017 Jared Rummler - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - import android.Manifest; - import android.content.Context; - import android.content.SharedPreferences; - import android.content.pm.PackageManager; - import android.net.ConnectivityManager; - import android.net.NetworkInfo; - import android.os.Build; - import android.os.Handler; - import android.os.Looper; - import android.support.annotation.WorkerThread; - import android.text.TextUtils; - import java.io.BufferedReader; - import java.io.IOException; - import java.io.InputStreamReader; - import java.net.HttpURLConnection; - import java.net.URL; - import java.util.Locale; - import org.json.JSONArray; - import org.json.JSONException; - import org.json.JSONObject; - -// @formatter:off -/** - *

Get the consumer friendly name of an Android device.

- * - *

On many popular devices the market name of the device is not available. For example, on the - * Samsung Galaxy S6 the value of {@link Build#MODEL} could be "SM-G920F", "SM-G920I", "SM-G920W8", - * etc.

- * - *

See the usages below to get the consumer friends name of a device:

- * - *

Get the name of the current device:

- * - *
- * String deviceName = DeviceName.getDeviceName();
- * 
- * - *

The above code will get the correct device name for the top 600 Android devices. If the - * device is unrecognized, then Build.MODEL is returned.

- * - *

Get the name of a device using the device's codename:

- * - *
- * // Retruns "Moto X Style"
- * DeviceName.getDeviceName("clark", "Unknown device");
- * 
- * - *

Get information about the device:

- * - *
- * DeviceName.with(context).request(new DeviceName.Callback() {
- *
- *   @Override public void onFinished(DeviceName.DeviceInfo info, Exception error) {
- *     String manufacturer = info.manufacturer;  // "Samsung"
- *     String name = info.marketName;            // "Galaxy S6 Edge"
- *     String model = info.model;                // "SM-G925I"
- *     String codename = info.codename;          // "zerolte"
- *     String deviceName = info.getName();       // "Galaxy S6 Edge"
- *     // FYI: We are on the UI thread.
- *   }
- * });
- * 
- * - *

The above code loads JSON from a generated list of device names based on Google's maintained - * list. It will be up-to-date with Google's supported device list so that you will get the correct - * name for new or unknown devices. This supports over 10,000 devices.

- * - *

This will only make a network call once. The value is saved to SharedPreferences for future - * calls.

- */ - -public class DeviceName { - - // @formatter:on - - // JSON which is derived from Google's PDF document which contains all devices on Google Play. - // To get the URL to the JSON file which contains information about the device name: - // String url = String.format(DEVICE_JSON_URL, Build.DEVICE); - private static final String DEVICE_JSON_URL = - "https://raw.githubusercontent.com/jaredrummler/AndroidDeviceNames/master/json/devices/%s.json"; - - // Preference filename for storing device info so we don't need to download it again. - private static final String SHARED_PREF_NAME = "device_names"; - - /** - * Create a new request to get information about a device. - * - * @param context - * the application context - * @return a new Request instance. - */ - public static Request with(Context context) { - return new Request(context.getApplicationContext()); - } - - /** - * Get the consumer friendly name of the device. - * - * @return the market name of the current device. - * @see #getDeviceName(String, String) - */ - public static String getDeviceName() { - return getDeviceName(Build.DEVICE, Build.MODEL, capitalize(Build.MODEL)); - } - - /** - * Get the consumer friendly name of a device. - * - * @param codename - * the value of the system property "ro.product.device" ({@link Build#DEVICE}) - * or - * the value of the system property "ro.product.model" ({@link Build#MODEL}) - * @param fallback - * the fallback name if the device is unknown. Usually the value of the system property - * "ro.product.model" ({@link Build#MODEL}) - * @return the market name of a device or {@code fallback} if the device is unknown. - */ - public static String getDeviceName(String codename, String fallback) { - return getDeviceName(codename, codename, fallback); - } - - /** - * Get the consumer friendly name of a device. - * - * @param codename - * the value of the system property "ro.product.device" ({@link Build#DEVICE}). - * @param model - * the value of the system property "ro.product.model" ({@link Build#MODEL}). - * @param fallback - * the fallback name if the device is unknown. Usually the value of the system property - * "ro.product.model" ({@link Build#MODEL}) - * @return the market name of a device or {@code fallback} if the device is unknown. - */ - public static String getDeviceName(String codename, String model, String fallback) { - // ---------------------------------------------------------------------------- - // Acer - if ((codename != null && codename.equals("acer_S57")) - || (model != null && model.equals("S57"))) { - return "Liquid Jade Z"; - } - if ((codename != null && codename.equals("acer_t08")) - || (model != null && model.equals("T08"))) { - return "Liquid Zest Plus"; - } - // ---------------------------------------------------------------------------- - // Asus - if ((codename != null && (codename.equals("grouper") - || codename.equals("tilapia")))) { - return "Nexus 7 (2012)"; - } - if ((codename != null && (codename.equals("deb") - || codename.equals("flo")))) { - return "Nexus 7 (2013)"; - } - // ---------------------------------------------------------------------------- - // Google - if ((codename != null && codename.equals("sailfish"))) { - return "Pixel"; - } - if ((codename != null && codename.equals("walleye"))) { - return "Pixel 2"; - } - if ((codename != null && codename.equals("taimen"))) { - return "Pixel 2 XL"; - } - if ((codename != null && codename.equals("dragon"))) { - return "Pixel C"; - } - if ((codename != null && codename.equals("marlin"))) { - return "Pixel XL"; - } - // ---------------------------------------------------------------------------- - // HTC - if ((codename != null && codename.equals("flounder"))) { - return "Nexus 9"; - } - // ---------------------------------------------------------------------------- - // Huawei - if ((codename != null && (codename.equals("HWBND-H"))) - || (model != null && (model.equals("BND-L21") - || model.equals("BND-L24")))) { - return "Honor 7X"; - } - if (model != null && model.contains("WAS-LX")) { - return "P10 lite"; - } - if ((codename != null && codename.equals("HWBKL")) - || (model != null && model.equals("BKL-L09"))) { - return "Honor View 10"; - } - if ((codename != null && (codename.equals("HWALP"))) - || (model != null && (model.equals("ALP-AL00") - || model.equals("ALP-L09") - || model.equals("ALP-L29") - || model.equals("ALP-TL00")))) { - return "Mate 10"; - } - if ((codename != null && (codename.equals("HWMHA"))) - || (model != null && (model.equals("MHA-AL00") - || model.equals("MHA-L09") - || model.equals("MHA-L29") - || model.equals("MHA-TL00")))) { - return "Mate 9"; - } - if ((codename != null && codename.equals("angler"))) { - return "Nexus 6P"; - } - // ---------------------------------------------------------------------------- - // LGE - if ((codename != null && (codename.equals("g2"))) - || (model != null && (model.equals("LG-D800") - || model.equals("LG-D801") - || model.equals("LG-D802") - || model.equals("LG-D802T") - || model.equals("LG-D802TR") - || model.equals("LG-D803") - || model.equals("LG-D805") - || model.equals("LG-D806") - || model.equals("LG-F320K") - || model.equals("LG-F320L") - || model.equals("LG-F320S") - || model.equals("LG-LS980") - || model.equals("VS980 4G")))) { - return "LG G2"; - } - if ((codename != null && (codename.equals("g3"))) - || (model != null && (model.equals("AS985") - || model.equals("LG-AS990") - || model.equals("LG-D850") - || model.equals("LG-D851") - || model.equals("LG-D852") - || model.equals("LG-D852G") - || model.equals("LG-D855") - || model.equals("LG-D856") - || model.equals("LG-D857") - || model.equals("LG-D858") - || model.equals("LG-D858HK") - || model.equals("LG-D859") - || model.equals("LG-F400K") - || model.equals("LG-F400L") - || model.equals("LG-F400S") - || model.equals("LGL24") - || model.equals("LGLS990") - || model.equals("LGUS990") - || model.equals("LGV31") - || model.equals("VS985 4G")))) { - return "LG G3"; - } - if ((codename != null && (codename.equals("p1"))) - || (model != null && (model.equals("AS986") - || model.equals("LG-AS811") - || model.equals("LG-AS991") - || model.equals("LG-F500K") - || model.equals("LG-F500L") - || model.equals("LG-F500S") - || model.equals("LG-H810") - || model.equals("LG-H811") - || model.equals("LG-H812") - || model.equals("LG-H815") - || model.equals("LG-H818") - || model.equals("LG-H819") - || model.equals("LGLS991") - || model.equals("LGUS991") - || model.equals("LGV32") - || model.equals("VS986")))) { - return "LG G4"; - } - if ((codename != null && (codename.equals("h1"))) - || (model != null && (model.equals("LG-F700K") - || model.equals("LG-F700L") - || model.equals("LG-F700S") - || model.equals("LG-H820") - || model.equals("LG-H820PR") - || model.equals("LG-H830") - || model.equals("LG-H831") - || model.equals("LG-H850") - || model.equals("LG-H858") - || model.equals("LG-H860") - || model.equals("LG-H868") - || model.equals("LGAS992") - || model.equals("LGLS992") - || model.equals("LGUS992") - || model.equals("RS988") - || model.equals("VS987")))) { - return "LG G5"; - } - if ((codename != null && (codename.equals("lucye"))) - || (model != null && (model.equals("LG-AS993") - || model.equals("LG-H870") - || model.equals("LG-H870AR") - || model.equals("LG-H870DS") - || model.equals("LG-H870I") - || model.equals("LG-H870S") - || model.equals("LG-H871") - || model.equals("LG-H872") - || model.equals("LG-H872PR") - || model.equals("LG-H873") - || model.equals("LG-LS993") - || model.equals("LGM-G600K") - || model.equals("LGM-G600L") - || model.equals("LGM-G600S") - || model.equals("LGUS997") - || model.equals("VS988")))) { - return "LG G6"; - } - if ((codename != null && codename.equals("mako"))) { - return "Nexus 4"; - } - if ((codename != null && codename.equals("hammerhead"))) { - return "Nexus 5"; - } - if ((codename != null && codename.equals("bullhead"))) { - return "Nexus 5X"; - } - // ---------------------------------------------------------------------------- - // Motorola - if ((codename != null && codename.equals("shamu"))) { - return "Nexus 6"; - } - // ---------------------------------------------------------------------------- - // OnePlus - if ((codename != null && codename.equals("OnePlus3")) - || (model != null && model.equals("ONEPLUS A3000"))) { - return "OnePlus3"; - } - if ((codename != null && codename.equals("OnePlus3T")) - || (model != null && model.equals("ONEPLUS A3000"))) { - return "OnePlus3T"; - } - if ((codename != null && codename.equals("OnePlus5")) - || (model != null && model.equals("ONEPLUS A5000"))) { - return "OnePlus5"; - } - if ((codename != null && codename.equals("OnePlus5T")) - || (model != null && model.equals("ONEPLUS A5010"))) { - return "OnePlus5T"; - } - // ---------------------------------------------------------------------------- - // Samsung - if ((codename != null && (codename.equals("a53g") - || codename.equals("a5lte") - || codename.equals("a5ltechn") - || codename.equals("a5ltectc") - || codename.equals("a5ltezh") - || codename.equals("a5ltezt") - || codename.equals("a5ulte") - || codename.equals("a5ultebmc") - || codename.equals("a5ultektt") - || codename.equals("a5ultelgt") - || codename.equals("a5ulteskt"))) - || (model != null && (model.equals("SM-A5000") - || model.equals("SM-A5009") - || model.equals("SM-A500F") - || model.equals("SM-A500F1") - || model.equals("SM-A500FU") - || model.equals("SM-A500G") - || model.equals("SM-A500H") - || model.equals("SM-A500K") - || model.equals("SM-A500L") - || model.equals("SM-A500M") - || model.equals("SM-A500S") - || model.equals("SM-A500W") - || model.equals("SM-A500X") - || model.equals("SM-A500XZ") - || model.equals("SM-A500Y") - || model.equals("SM-A500YZ")))) { - return "Galaxy A5"; - } - if ((codename != null && (codename.equals("vivaltods5m"))) - || (model != null && (model.equals("SM-G313HU") - || model.equals("SM-G313HY") - || model.equals("SM-G313M") - || model.equals("SM-G313MY")))) { - return "Galaxy Ace 4"; - } - if ((codename != null && (codename.equals("GT-S6352") - || codename.equals("GT-S6802") - || codename.equals("GT-S6802B") - || codename.equals("SCH-I579") - || codename.equals("SCH-I589") - || codename.equals("SCH-i579") - || codename.equals("SCH-i589"))) - || (model != null && (model.equals("GT-S6352") - || model.equals("GT-S6802") - || model.equals("GT-S6802B") - || model.equals("SCH-I589") - || model.equals("SCH-i579") - || model.equals("SCH-i589")))) { - return "Galaxy Ace Duos"; - } - if ((codename != null && (codename.equals("GT-S7500") - || codename.equals("GT-S7500L") - || codename.equals("GT-S7500T") - || codename.equals("GT-S7500W") - || codename.equals("GT-S7508"))) - || (model != null && (model.equals("GT-S7500") - || model.equals("GT-S7500L") - || model.equals("GT-S7500T") - || model.equals("GT-S7500W") - || model.equals("GT-S7508")))) { - return "Galaxy Ace Plus"; - } - if ((codename != null && (codename.equals("heat3gtfnvzw") - || codename.equals("heatnfc3g") - || codename.equals("heatqlte"))) - || (model != null && (model.equals("SM-G310HN") - || model.equals("SM-G357FZ") - || model.equals("SM-S765C") - || model.equals("SM-S766C")))) { - return "Galaxy Ace Style"; - } - if ((codename != null && (codename.equals("vivalto3g") - || codename.equals("vivalto3mve3g") - || codename.equals("vivalto5mve3g") - || codename.equals("vivaltolte") - || codename.equals("vivaltonfc3g"))) - || (model != null && (model.equals("SM-G313F") - || model.equals("SM-G313HN") - || model.equals("SM-G313ML") - || model.equals("SM-G313MU") - || model.equals("SM-G316H") - || model.equals("SM-G316HU") - || model.equals("SM-G316M") - || model.equals("SM-G316MY")))) { - return "Galaxy Ace4"; - } - if ((codename != null && (codename.equals("core33g") - || codename.equals("coreprimelte") - || codename.equals("coreprimelteaio") - || codename.equals("coreprimeltelra") - || codename.equals("coreprimeltespr") - || codename.equals("coreprimeltetfnvzw") - || codename.equals("coreprimeltevzw") - || codename.equals("coreprimeve3g") - || codename.equals("coreprimevelte") - || codename.equals("cprimeltemtr") - || codename.equals("cprimeltetmo") - || codename.equals("rossalte") - || codename.equals("rossaltectc") - || codename.equals("rossaltexsa"))) - || (model != null && (model.equals("SAMSUNG-SM-G360AZ") - || model.equals("SM-G3606") - || model.equals("SM-G3608") - || model.equals("SM-G3609") - || model.equals("SM-G360F") - || model.equals("SM-G360FY") - || model.equals("SM-G360GY") - || model.equals("SM-G360H") - || model.equals("SM-G360HU") - || model.equals("SM-G360M") - || model.equals("SM-G360P") - || model.equals("SM-G360R6") - || model.equals("SM-G360T") - || model.equals("SM-G360T1") - || model.equals("SM-G360V") - || model.equals("SM-G361F") - || model.equals("SM-G361H") - || model.equals("SM-G361HU") - || model.equals("SM-G361M") - || model.equals("SM-S820L")))) { - return "Galaxy Core Prime"; - } - if ((codename != null && (codename.equals("kanas") - || codename.equals("kanas3g") - || codename.equals("kanas3gcmcc") - || codename.equals("kanas3gctc") - || codename.equals("kanas3gnfc"))) - || (model != null && (model.equals("SM-G3556D") - || model.equals("SM-G3558") - || model.equals("SM-G3559") - || model.equals("SM-G355H") - || model.equals("SM-G355HN") - || model.equals("SM-G355HQ") - || model.equals("SM-G355M")))) { - return "Galaxy Core2"; - } - if ((codename != null && (codename.equals("e53g") - || codename.equals("e5lte") - || codename.equals("e5ltetfnvzw") - || codename.equals("e5ltetw"))) - || (model != null && (model.equals("SM-E500F") - || model.equals("SM-E500H") - || model.equals("SM-E500M") - || model.equals("SM-E500YZ") - || model.equals("SM-S978L")))) { - return "Galaxy E5"; - } - if ((codename != null && (codename.equals("e73g") - || codename.equals("e7lte") - || codename.equals("e7ltechn") - || codename.equals("e7ltectc") - || codename.equals("e7ltehktw"))) - || (model != null && (model.equals("SM-E7000") - || model.equals("SM-E7009") - || model.equals("SM-E700F") - || model.equals("SM-E700H") - || model.equals("SM-E700M")))) { - return "Galaxy E7"; - } - if ((codename != null && (codename.equals("SCH-I629") - || codename.equals("nevis") - || codename.equals("nevis3g") - || codename.equals("nevis3gcmcc") - || codename.equals("nevisds") - || codename.equals("nevisnvess") - || codename.equals("nevisp") - || codename.equals("nevisvess") - || codename.equals("nevisw"))) - || (model != null && (model.equals("GT-S6790") - || model.equals("GT-S6790E") - || model.equals("GT-S6790L") - || model.equals("GT-S6790N") - || model.equals("GT-S6810") - || model.equals("GT-S6810B") - || model.equals("GT-S6810E") - || model.equals("GT-S6810L") - || model.equals("GT-S6810M") - || model.equals("GT-S6810P") - || model.equals("GT-S6812") - || model.equals("GT-S6812B") - || model.equals("GT-S6812C") - || model.equals("GT-S6812i") - || model.equals("GT-S6818") - || model.equals("GT-S6818V") - || model.equals("SCH-I629")))) { - return "Galaxy Fame"; - } - if ((codename != null && codename.equals("grandprimelteatt")) - || (model != null && model.equals("SAMSUNG-SM-G530A"))) { - return "Galaxy Go Prime"; - } - if ((codename != null && (codename.equals("baffinlite") - || codename.equals("baffinlitedtv") - || codename.equals("baffinq3g"))) - || (model != null && (model.equals("GT-I9060") - || model.equals("GT-I9060L") - || model.equals("GT-I9063T") - || model.equals("GT-I9082C") - || model.equals("GT-I9168") - || model.equals("GT-I9168I")))) { - return "Galaxy Grand Neo"; - } - if ((codename != null && (codename.equals("fortuna3g") - || codename.equals("fortuna3gdtv") - || codename.equals("fortunalte") - || codename.equals("fortunaltectc") - || codename.equals("fortunaltezh") - || codename.equals("fortunaltezt") - || codename.equals("fortunave3g") - || codename.equals("gprimelteacg") - || codename.equals("gprimeltecan") - || codename.equals("gprimeltemtr") - || codename.equals("gprimeltespr") - || codename.equals("gprimeltetfnvzw") - || codename.equals("gprimeltetmo") - || codename.equals("gprimelteusc") - || codename.equals("grandprimelte") - || codename.equals("grandprimelteaio") - || codename.equals("grandprimeve3g") - || codename.equals("grandprimeve3gdtv") - || codename.equals("grandprimevelte") - || codename.equals("grandprimevelteltn") - || codename.equals("grandprimeveltezt"))) - || (model != null && (model.equals("SAMSUNG-SM-G530AZ") - || model.equals("SM-G5306W") - || model.equals("SM-G5308W") - || model.equals("SM-G5309W") - || model.equals("SM-G530BT") - || model.equals("SM-G530F") - || model.equals("SM-G530FZ") - || model.equals("SM-G530H") - || model.equals("SM-G530M") - || model.equals("SM-G530MU") - || model.equals("SM-G530P") - || model.equals("SM-G530R4") - || model.equals("SM-G530R7") - || model.equals("SM-G530T") - || model.equals("SM-G530T1") - || model.equals("SM-G530W") - || model.equals("SM-G530Y") - || model.equals("SM-G531BT") - || model.equals("SM-G531F") - || model.equals("SM-G531H") - || model.equals("SM-G531M") - || model.equals("SM-G531Y") - || model.equals("SM-S920L") - || model.equals("gprimelteacg")))) { - return "Galaxy Grand Prime"; - } - if ((codename != null && (codename.equals("ms013g") - || codename.equals("ms013gdtv") - || codename.equals("ms013gss") - || codename.equals("ms01lte") - || codename.equals("ms01ltektt") - || codename.equals("ms01ltelgt") - || codename.equals("ms01lteskt"))) - || (model != null && (model.equals("SM-G710") - || model.equals("SM-G7102") - || model.equals("SM-G7102T") - || model.equals("SM-G7105") - || model.equals("SM-G7105H") - || model.equals("SM-G7105L") - || model.equals("SM-G7106") - || model.equals("SM-G7108") - || model.equals("SM-G7109") - || model.equals("SM-G710K") - || model.equals("SM-G710L") - || model.equals("SM-G710S")))) { - return "Galaxy Grand2"; - } - if ((codename != null && (codename.equals("j13g") - || codename.equals("j13gtfnvzw") - || codename.equals("j1lte") - || codename.equals("j1nlte") - || codename.equals("j1qltevzw") - || codename.equals("j1xlte") - || codename.equals("j1xlteaio") - || codename.equals("j1xlteatt") - || codename.equals("j1xltecan") - || codename.equals("j1xqltespr") - || codename.equals("j1xqltetfnvzw"))) - || (model != null && (model.equals("SAMSUNG-SM-J120A") - || model.equals("SAMSUNG-SM-J120AZ") - || model.equals("SM-J100F") - || model.equals("SM-J100FN") - || model.equals("SM-J100G") - || model.equals("SM-J100H") - || model.equals("SM-J100M") - || model.equals("SM-J100ML") - || model.equals("SM-J100MU") - || model.equals("SM-J100VPP") - || model.equals("SM-J100Y") - || model.equals("SM-J120F") - || model.equals("SM-J120FN") - || model.equals("SM-J120M") - || model.equals("SM-J120P") - || model.equals("SM-J120W") - || model.equals("SM-S120VL") - || model.equals("SM-S777C")))) { - return "Galaxy J1"; - } - if ((codename != null && (codename.equals("j1acelte") - || codename.equals("j1acelteltn") - || codename.equals("j1acevelte") - || codename.equals("j1pop3g"))) - || (model != null && (model.equals("SM-J110F") - || model.equals("SM-J110G") - || model.equals("SM-J110H") - || model.equals("SM-J110L") - || model.equals("SM-J110M") - || model.equals("SM-J111F") - || model.equals("SM-J111M")))) { - return "Galaxy J1 Ace"; - } - if ((codename != null && (codename.equals("j53g") - || codename.equals("j5lte") - || codename.equals("j5ltechn") - || codename.equals("j5ltekx") - || codename.equals("j5nlte") - || codename.equals("j5ylte"))) - || (model != null && (model.equals("SM-J5007") - || model.equals("SM-J5008") - || model.equals("SM-J500F") - || model.equals("SM-J500FN") - || model.equals("SM-J500G") - || model.equals("SM-J500H") - || model.equals("SM-J500M") - || model.equals("SM-J500N0") - || model.equals("SM-J500Y")))) { - return "Galaxy J5"; - } - if ((codename != null && (codename.equals("j75ltektt") - || codename.equals("j7e3g") - || codename.equals("j7elte") - || codename.equals("j7ltechn"))) - || (model != null && (model.equals("SM-J7008") - || model.equals("SM-J700F") - || model.equals("SM-J700H") - || model.equals("SM-J700K") - || model.equals("SM-J700M")))) { - return "Galaxy J7"; - } - if ((codename != null && (codename.equals("maguro") - || codename.equals("toro") - || codename.equals("toroplus"))) - || (model != null && (model.equals("Galaxy X")))) { - return "Galaxy Nexus"; - } - if ((codename != null && (codename.equals("lt033g") - || codename.equals("lt03ltektt") - || codename.equals("lt03ltelgt") - || codename.equals("lt03lteskt") - || codename.equals("p4notelte") - || codename.equals("p4noteltektt") - || codename.equals("p4noteltelgt") - || codename.equals("p4notelteskt") - || codename.equals("p4noteltespr") - || codename.equals("p4notelteusc") - || codename.equals("p4noteltevzw") - || codename.equals("p4noterf") - || codename.equals("p4noterfktt") - || codename.equals("p4notewifi") - || codename.equals("p4notewifi43241any") - || codename.equals("p4notewifiany") - || codename.equals("p4notewifiktt") - || codename.equals("p4notewifiww"))) - || (model != null && (model.equals("GT-N8000") - || model.equals("GT-N8005") - || model.equals("GT-N8010") - || model.equals("GT-N8013") - || model.equals("GT-N8020") - || model.equals("SCH-I925") - || model.equals("SCH-I925U") - || model.equals("SHV-E230K") - || model.equals("SHV-E230L") - || model.equals("SHV-E230S") - || model.equals("SHW-M480K") - || model.equals("SHW-M480W") - || model.equals("SHW-M485W") - || model.equals("SHW-M486W") - || model.equals("SM-P601") - || model.equals("SM-P602") - || model.equals("SM-P605K") - || model.equals("SM-P605L") - || model.equals("SM-P605S") - || model.equals("SPH-P600")))) { - return "Galaxy Note 10.1"; - } - if ((codename != null && (codename.equals("SC-01G") - || codename.equals("SCL24") - || codename.equals("tbeltektt") - || codename.equals("tbeltelgt") - || codename.equals("tbelteskt") - || codename.equals("tblte") - || codename.equals("tblteatt") - || codename.equals("tbltecan") - || codename.equals("tbltechn") - || codename.equals("tbltespr") - || codename.equals("tbltetmo") - || codename.equals("tblteusc") - || codename.equals("tbltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-N915A") - || model.equals("SC-01G") - || model.equals("SCL24") - || model.equals("SM-N9150") - || model.equals("SM-N915F") - || model.equals("SM-N915FY") - || model.equals("SM-N915G") - || model.equals("SM-N915K") - || model.equals("SM-N915L") - || model.equals("SM-N915P") - || model.equals("SM-N915R4") - || model.equals("SM-N915S") - || model.equals("SM-N915T") - || model.equals("SM-N915T3") - || model.equals("SM-N915V") - || model.equals("SM-N915W8") - || model.equals("SM-N915X")))) { - return "Galaxy Note Edge"; - } - if ((codename != null && (codename.equals("v1a3g") - || codename.equals("v1awifi") - || codename.equals("v1awifikx") - || codename.equals("viennalte") - || codename.equals("viennalteatt") - || codename.equals("viennaltekx") - || codename.equals("viennaltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-P907A") - || model.equals("SM-P900") - || model.equals("SM-P901") - || model.equals("SM-P905") - || model.equals("SM-P905F0") - || model.equals("SM-P905M") - || model.equals("SM-P905V")))) { - return "Galaxy Note Pro 12.2"; - } - if ((codename != null && (codename.equals("tre3caltektt") - || codename.equals("tre3caltelgt") - || codename.equals("tre3calteskt") - || codename.equals("tre3g") - || codename.equals("trelte") - || codename.equals("treltektt") - || codename.equals("treltelgt") - || codename.equals("trelteskt") - || codename.equals("trhplte") - || codename.equals("trlte") - || codename.equals("trlteatt") - || codename.equals("trltecan") - || codename.equals("trltechn") - || codename.equals("trltechnzh") - || codename.equals("trltespr") - || codename.equals("trltetmo") - || codename.equals("trlteusc") - || codename.equals("trltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-N910A") - || model.equals("SM-N9100") - || model.equals("SM-N9106W") - || model.equals("SM-N9108V") - || model.equals("SM-N9109W") - || model.equals("SM-N910C") - || model.equals("SM-N910F") - || model.equals("SM-N910G") - || model.equals("SM-N910H") - || model.equals("SM-N910K") - || model.equals("SM-N910L") - || model.equals("SM-N910P") - || model.equals("SM-N910R4") - || model.equals("SM-N910S") - || model.equals("SM-N910T") - || model.equals("SM-N910T2") - || model.equals("SM-N910T3") - || model.equals("SM-N910U") - || model.equals("SM-N910V") - || model.equals("SM-N910W8") - || model.equals("SM-N910X") - || model.equals("SM-N916K") - || model.equals("SM-N916L") - || model.equals("SM-N916S")))) { - return "Galaxy Note4"; - } - if ((codename != null && (codename.equals("noblelte") - || codename.equals("noblelteacg") - || codename.equals("noblelteatt") - || codename.equals("nobleltebmc") - || codename.equals("nobleltechn") - || codename.equals("nobleltecmcc") - || codename.equals("nobleltehk") - || codename.equals("nobleltektt") - || codename.equals("nobleltelgt") - || codename.equals("nobleltelra") - || codename.equals("noblelteskt") - || codename.equals("nobleltespr") - || codename.equals("nobleltetmo") - || codename.equals("noblelteusc") - || codename.equals("nobleltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-N920A") - || model.equals("SM-N9200") - || model.equals("SM-N9208") - || model.equals("SM-N920C") - || model.equals("SM-N920F") - || model.equals("SM-N920G") - || model.equals("SM-N920I") - || model.equals("SM-N920K") - || model.equals("SM-N920L") - || model.equals("SM-N920P") - || model.equals("SM-N920R4") - || model.equals("SM-N920R6") - || model.equals("SM-N920R7") - || model.equals("SM-N920S") - || model.equals("SM-N920T") - || model.equals("SM-N920V") - || model.equals("SM-N920W8") - || model.equals("SM-N920X")))) { - return "Galaxy Note5"; - } - if ((codename != null && (codename.equals("SC-01J") - || codename.equals("SCV34") - || codename.equals("gracelte") - || codename.equals("graceltektt") - || codename.equals("graceltelgt") - || codename.equals("gracelteskt") - || codename.equals("graceqlteacg") - || codename.equals("graceqlteatt") - || codename.equals("graceqltebmc") - || codename.equals("graceqltechn") - || codename.equals("graceqltedcm") - || codename.equals("graceqltelra") - || codename.equals("graceqltespr") - || codename.equals("graceqltetfnvzw") - || codename.equals("graceqltetmo") - || codename.equals("graceqlteue") - || codename.equals("graceqlteusc") - || codename.equals("graceqltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-N930A") - || model.equals("SC-01J") - || model.equals("SCV34") - || model.equals("SGH-N037") - || model.equals("SM-N9300") - || model.equals("SM-N930F") - || model.equals("SM-N930K") - || model.equals("SM-N930L") - || model.equals("SM-N930P") - || model.equals("SM-N930R4") - || model.equals("SM-N930R6") - || model.equals("SM-N930R7") - || model.equals("SM-N930S") - || model.equals("SM-N930T") - || model.equals("SM-N930U") - || model.equals("SM-N930V") - || model.equals("SM-N930VL") - || model.equals("SM-N930W8") - || model.equals("SM-N930X")))) { - return "Galaxy Note7"; - } - if ((codename != null && (codename.equals("SC-01K") - || codename.equals("SCV37") - || codename.equals("greatlte") - || codename.equals("greatlteks") - || codename.equals("greatqlte") - || codename.equals("greatqltechn") - || codename.equals("greatqltecmcc") - || codename.equals("greatqltecs") - || codename.equals("greatqlteue"))) - || (model != null && (model.equals("SC-01K") - || model.equals("SCV37") - || model.equals("SM-N9500") - || model.equals("SM-N9508") - || model.equals("SM-N950F") - || model.equals("SM-N950N") - || model.equals("SM-N950U") - || model.equals("SM-N950U1") - || model.equals("SM-N950W") - || model.equals("SM-N950XN")))) { - return "Galaxy Note8"; - } - if ((codename != null && (codename.equals("o5lte") - || codename.equals("o5ltechn") - || codename.equals("o5prolte") - || codename.equals("on5ltemtr") - || codename.equals("on5ltetfntmo") - || codename.equals("on5ltetmo"))) - || (model != null && (model.equals("SM-G5500") - || model.equals("SM-G550FY") - || model.equals("SM-G550T") - || model.equals("SM-G550T1") - || model.equals("SM-G550T2") - || model.equals("SM-S550TL")))) { - return "Galaxy On5"; - } - if ((codename != null && (codename.equals("o7lte") - || codename.equals("o7ltechn") - || codename.equals("on7elte"))) - || (model != null && (model.equals("SM-G6000") - || model.equals("SM-G600F") - || model.equals("SM-G600FY")))) { - return "Galaxy On7"; - } - if ((codename != null && (codename.equals("GT-I9000") - || codename.equals("GT-I9000B") - || codename.equals("GT-I9000M") - || codename.equals("GT-I9000T") - || codename.equals("GT-I9003") - || codename.equals("GT-I9003L") - || codename.equals("GT-I9008L") - || codename.equals("GT-I9010") - || codename.equals("GT-I9018") - || codename.equals("GT-I9050") - || codename.equals("SC-02B") - || codename.equals("SCH-I500") - || codename.equals("SCH-S950C") - || codename.equals("SCH-i909") - || codename.equals("SGH-I897") - || codename.equals("SGH-T959V") - || codename.equals("SGH-T959W") - || codename.equals("SHW-M110S") - || codename.equals("SHW-M190S") - || codename.equals("SPH-D700") - || codename.equals("loganlte"))) - || (model != null && (model.equals("GT-I9000") - || model.equals("GT-I9000B") - || model.equals("GT-I9000M") - || model.equals("GT-I9000T") - || model.equals("GT-I9003") - || model.equals("GT-I9003L") - || model.equals("GT-I9008L") - || model.equals("GT-I9010") - || model.equals("GT-I9018") - || model.equals("GT-I9050") - || model.equals("GT-S7275") - || model.equals("SAMSUNG-SGH-I897") - || model.equals("SC-02B") - || model.equals("SCH-I500") - || model.equals("SCH-S950C") - || model.equals("SCH-i909") - || model.equals("SGH-T959V") - || model.equals("SGH-T959W") - || model.equals("SHW-M110S") - || model.equals("SHW-M190S") - || model.equals("SPH-D700")))) { - return "Galaxy S"; - } - if ((codename != null && (codename.equals("kylechn") - || codename.equals("kyleopen") - || codename.equals("kyletdcmcc"))) - || (model != null && (model.equals("GT-S7562") - || model.equals("GT-S7568")))) { - return "Galaxy S Duos"; - } - if ((codename != null && (codename.equals("kyleprods"))) - || (model != null && (model.equals("GT-S7582") - || model.equals("GT-S7582L")))) { - return "Galaxy S Duos2"; - } - if ((codename != null && codename.equals("vivalto3gvn")) - || (model != null && model.equals("SM-G313HZ"))) { - return "Galaxy S Duos3"; - } - if ((codename != null && (codename.equals("SC-03E") - || codename.equals("c1att") - || codename.equals("c1ktt") - || codename.equals("c1lgt") - || codename.equals("c1skt") - || codename.equals("d2att") - || codename.equals("d2can") - || codename.equals("d2cri") - || codename.equals("d2dcm") - || codename.equals("d2lteMetroPCS") - || codename.equals("d2lterefreshspr") - || codename.equals("d2ltetmo") - || codename.equals("d2mtr") - || codename.equals("d2spi") - || codename.equals("d2spr") - || codename.equals("d2tfnspr") - || codename.equals("d2tfnvzw") - || codename.equals("d2tmo") - || codename.equals("d2usc") - || codename.equals("d2vmu") - || codename.equals("d2vzw") - || codename.equals("d2xar") - || codename.equals("m0") - || codename.equals("m0apt") - || codename.equals("m0chn") - || codename.equals("m0cmcc") - || codename.equals("m0ctc") - || codename.equals("m0ctcduos") - || codename.equals("m0skt") - || codename.equals("m3") - || codename.equals("m3dcm"))) - || (model != null && (model.equals("GT-I9300") - || model.equals("GT-I9300T") - || model.equals("GT-I9305") - || model.equals("GT-I9305N") - || model.equals("GT-I9305T") - || model.equals("GT-I9308") - || model.equals("Gravity") - || model.equals("GravityQuad") - || model.equals("SAMSUNG-SGH-I747") - || model.equals("SC-03E") - || model.equals("SC-06D") - || model.equals("SCH-I535") - || model.equals("SCH-I535PP") - || model.equals("SCH-I939") - || model.equals("SCH-I939D") - || model.equals("SCH-L710") - || model.equals("SCH-R530C") - || model.equals("SCH-R530M") - || model.equals("SCH-R530U") - || model.equals("SCH-R530X") - || model.equals("SCH-S960L") - || model.equals("SCH-S968C") - || model.equals("SGH-I747M") - || model.equals("SGH-I748") - || model.equals("SGH-T999") - || model.equals("SGH-T999L") - || model.equals("SGH-T999N") - || model.equals("SGH-T999V") - || model.equals("SHV-E210K") - || model.equals("SHV-E210L") - || model.equals("SHV-E210S") - || model.equals("SHW-M440S") - || model.equals("SPH-L710") - || model.equals("SPH-L710T")))) { - return "Galaxy S3"; - } - if ((codename != null && (codename.equals("golden") - || codename.equals("goldenlteatt") - || codename.equals("goldenltebmc") - || codename.equals("goldenltevzw") - || codename.equals("goldenve3g"))) - || (model != null && (model.equals("GT-I8190") - || model.equals("GT-I8190L") - || model.equals("GT-I8190N") - || model.equals("GT-I8190T") - || model.equals("GT-I8200L") - || model.equals("SAMSUNG-SM-G730A") - || model.equals("SM-G730V") - || model.equals("SM-G730W8")))) { - return "Galaxy S3 Mini"; - } - if ((codename != null && (codename.equals("goldenve3g") - || codename.equals("goldenvess3g"))) - || (model != null && (model.equals("GT-I8200") - || model.equals("GT-I8200N") - || model.equals("GT-I8200Q")))) { - return "Galaxy S3 Mini Value Edition"; - } - if ((codename != null && (codename.equals("s3ve3g") - || codename.equals("s3ve3gdd") - || codename.equals("s3ve3gds") - || codename.equals("s3ve3gdsdd"))) - || (model != null && (model.equals("GT-I9300I") - || model.equals("GT-I9301I") - || model.equals("GT-I9301Q")))) { - return "Galaxy S3 Neo"; - } - if ((codename != null && (codename.equals("SC-04E") - || codename.equals("ja3g") - || codename.equals("ja3gduosctc") - || codename.equals("jaltektt") - || codename.equals("jaltelgt") - || codename.equals("jalteskt") - || codename.equals("jflte") - || codename.equals("jflteMetroPCS") - || codename.equals("jflteaio") - || codename.equals("jflteatt") - || codename.equals("jfltecan") - || codename.equals("jfltecri") - || codename.equals("jfltecsp") - || codename.equals("jfltelra") - || codename.equals("jflterefreshspr") - || codename.equals("jfltespr") - || codename.equals("jfltetfnatt") - || codename.equals("jfltetfntmo") - || codename.equals("jfltetmo") - || codename.equals("jflteusc") - || codename.equals("jfltevzw") - || codename.equals("jfltevzwpp") - || codename.equals("jftdd") - || codename.equals("jfvelte") - || codename.equals("jfwifi") - || codename.equals("jsglte") - || codename.equals("ks01lte") - || codename.equals("ks01ltektt") - || codename.equals("ks01ltelgt"))) - || (model != null && (model.equals("GT-I9500") - || model.equals("GT-I9505") - || model.equals("GT-I9505X") - || model.equals("GT-I9506") - || model.equals("GT-I9507") - || model.equals("GT-I9507V") - || model.equals("GT-I9508") - || model.equals("GT-I9508C") - || model.equals("GT-I9508V") - || model.equals("GT-I9515") - || model.equals("GT-I9515L") - || model.equals("SAMSUNG-SGH-I337") - || model.equals("SAMSUNG-SGH-I337Z") - || model.equals("SC-04E") - || model.equals("SCH-I545") - || model.equals("SCH-I545L") - || model.equals("SCH-I545PP") - || model.equals("SCH-I959") - || model.equals("SCH-R970") - || model.equals("SCH-R970C") - || model.equals("SCH-R970X") - || model.equals("SGH-I337M") - || model.equals("SGH-M919") - || model.equals("SGH-M919N") - || model.equals("SGH-M919V") - || model.equals("SGH-S970G") - || model.equals("SHV-E300K") - || model.equals("SHV-E300L") - || model.equals("SHV-E300S") - || model.equals("SHV-E330K") - || model.equals("SHV-E330L") - || model.equals("SM-S975L") - || model.equals("SPH-L720") - || model.equals("SPH-L720T")))) { - return "Galaxy S4"; - } - if ((codename != null && (codename.equals("serrano3g") - || codename.equals("serranods") - || codename.equals("serranolte") - || codename.equals("serranoltebmc") - || codename.equals("serranoltektt") - || codename.equals("serranoltekx") - || codename.equals("serranoltelra") - || codename.equals("serranoltespr") - || codename.equals("serranolteusc") - || codename.equals("serranoltevzw") - || codename.equals("serranove3g") - || codename.equals("serranovelte") - || codename.equals("serranovolteatt"))) - || (model != null && (model.equals("GT-I9190") - || model.equals("GT-I9192") - || model.equals("GT-I9192I") - || model.equals("GT-I9195") - || model.equals("GT-I9195I") - || model.equals("GT-I9195L") - || model.equals("GT-I9195T") - || model.equals("GT-I9195X") - || model.equals("GT-I9197") - || model.equals("SAMSUNG-SGH-I257") - || model.equals("SCH-I435") - || model.equals("SCH-I435L") - || model.equals("SCH-R890") - || model.equals("SGH-I257M") - || model.equals("SHV-E370D") - || model.equals("SHV-E370K") - || model.equals("SPH-L520")))) { - return "Galaxy S4 Mini"; - } - if ((codename != null && (codename.equals("SC-04F") - || codename.equals("SCL23") - || codename.equals("k3g") - || codename.equals("klte") - || codename.equals("klteMetroPCS") - || codename.equals("klteacg") - || codename.equals("klteaio") - || codename.equals("klteatt") - || codename.equals("kltecan") - || codename.equals("klteduoszn") - || codename.equals("kltektt") - || codename.equals("kltelgt") - || codename.equals("kltelra") - || codename.equals("klteskt") - || codename.equals("kltespr") - || codename.equals("kltetfnvzw") - || codename.equals("kltetmo") - || codename.equals("klteusc") - || codename.equals("kltevzw") - || codename.equals("kwifi") - || codename.equals("lentisltektt") - || codename.equals("lentisltelgt") - || codename.equals("lentislteskt"))) - || (model != null && (model.equals("SAMSUNG-SM-G900A") - || model.equals("SAMSUNG-SM-G900AZ") - || model.equals("SC-04F") - || model.equals("SCL23") - || model.equals("SM-G9006W") - || model.equals("SM-G9008W") - || model.equals("SM-G9009W") - || model.equals("SM-G900F") - || model.equals("SM-G900FQ") - || model.equals("SM-G900H") - || model.equals("SM-G900I") - || model.equals("SM-G900K") - || model.equals("SM-G900L") - || model.equals("SM-G900M") - || model.equals("SM-G900MD") - || model.equals("SM-G900P") - || model.equals("SM-G900R4") - || model.equals("SM-G900R6") - || model.equals("SM-G900R7") - || model.equals("SM-G900S") - || model.equals("SM-G900T") - || model.equals("SM-G900T1") - || model.equals("SM-G900T3") - || model.equals("SM-G900T4") - || model.equals("SM-G900V") - || model.equals("SM-G900W8") - || model.equals("SM-G900X") - || model.equals("SM-G906K") - || model.equals("SM-G906L") - || model.equals("SM-G906S") - || model.equals("SM-S903VL")))) { - return "Galaxy S5"; - } - if ((codename != null && (codename.equals("s5neolte") - || codename.equals("s5neoltecan"))) - || (model != null && (model.equals("SM-G903F") - || model.equals("SM-G903M") - || model.equals("SM-G903W")))) { - return "Galaxy S5 Neo"; - } - if ((codename != null && (codename.equals("SC-05G") - || codename.equals("zeroflte") - || codename.equals("zeroflteacg") - || codename.equals("zeroflteaio") - || codename.equals("zeroflteatt") - || codename.equals("zerofltebmc") - || codename.equals("zerofltechn") - || codename.equals("zerofltectc") - || codename.equals("zerofltektt") - || codename.equals("zerofltelgt") - || codename.equals("zerofltelra") - || codename.equals("zerofltemtr") - || codename.equals("zeroflteskt") - || codename.equals("zerofltespr") - || codename.equals("zerofltetfnvzw") - || codename.equals("zerofltetmo") - || codename.equals("zeroflteusc") - || codename.equals("zerofltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-G920A") - || model.equals("SAMSUNG-SM-G920AZ") - || model.equals("SC-05G") - || model.equals("SM-G9200") - || model.equals("SM-G9208") - || model.equals("SM-G9209") - || model.equals("SM-G920F") - || model.equals("SM-G920I") - || model.equals("SM-G920K") - || model.equals("SM-G920L") - || model.equals("SM-G920P") - || model.equals("SM-G920R4") - || model.equals("SM-G920R6") - || model.equals("SM-G920R7") - || model.equals("SM-G920S") - || model.equals("SM-G920T") - || model.equals("SM-G920T1") - || model.equals("SM-G920V") - || model.equals("SM-G920W8") - || model.equals("SM-G920X") - || model.equals("SM-S906L") - || model.equals("SM-S907VL")))) { - return "Galaxy S6"; - } - if ((codename != null && (codename.equals("404SC") - || codename.equals("SC-04G") - || codename.equals("SCV31") - || codename.equals("zerolte") - || codename.equals("zerolteacg") - || codename.equals("zerolteatt") - || codename.equals("zeroltebmc") - || codename.equals("zeroltechn") - || codename.equals("zeroltektt") - || codename.equals("zeroltelgt") - || codename.equals("zeroltelra") - || codename.equals("zerolteskt") - || codename.equals("zeroltespr") - || codename.equals("zeroltetmo") - || codename.equals("zerolteusc") - || codename.equals("zeroltevzw"))) - || (model != null && (model.equals("404SC") - || model.equals("SAMSUNG-SM-G925A") - || model.equals("SC-04G") - || model.equals("SCV31") - || model.equals("SM-G9250") - || model.equals("SM-G925F") - || model.equals("SM-G925I") - || model.equals("SM-G925K") - || model.equals("SM-G925L") - || model.equals("SM-G925P") - || model.equals("SM-G925R4") - || model.equals("SM-G925R6") - || model.equals("SM-G925R7") - || model.equals("SM-G925S") - || model.equals("SM-G925T") - || model.equals("SM-G925V") - || model.equals("SM-G925W8") - || model.equals("SM-G925X")))) { - return "Galaxy S6 Edge"; - } - if ((codename != null && (codename.equals("zenlte") - || codename.equals("zenlteatt") - || codename.equals("zenltebmc") - || codename.equals("zenltechn") - || codename.equals("zenltektt") - || codename.equals("zenltekx") - || codename.equals("zenltelgt") - || codename.equals("zenlteskt") - || codename.equals("zenltespr") - || codename.equals("zenltetmo") - || codename.equals("zenlteusc") - || codename.equals("zenltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-G928A") - || model.equals("SM-G9280") - || model.equals("SM-G9287") - || model.equals("SM-G9287C") - || model.equals("SM-G928C") - || model.equals("SM-G928F") - || model.equals("SM-G928G") - || model.equals("SM-G928I") - || model.equals("SM-G928K") - || model.equals("SM-G928L") - || model.equals("SM-G928N0") - || model.equals("SM-G928P") - || model.equals("SM-G928R4") - || model.equals("SM-G928S") - || model.equals("SM-G928T") - || model.equals("SM-G928V") - || model.equals("SM-G928W8") - || model.equals("SM-G928X")))) { - return "Galaxy S6 Edge+"; - } - if ((codename != null && (codename.equals("herolte") - || codename.equals("heroltebmc") - || codename.equals("heroltektt") - || codename.equals("heroltelgt") - || codename.equals("herolteskt") - || codename.equals("heroqlteacg") - || codename.equals("heroqlteaio") - || codename.equals("heroqlteatt") - || codename.equals("heroqltecctvzw") - || codename.equals("heroqltechn") - || codename.equals("heroqltelra") - || codename.equals("heroqltemtr") - || codename.equals("heroqltespr") - || codename.equals("heroqltetfnvzw") - || codename.equals("heroqltetmo") - || codename.equals("heroqlteue") - || codename.equals("heroqlteusc") - || codename.equals("heroqltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-G930A") - || model.equals("SAMSUNG-SM-G930AZ") - || model.equals("SM-G9300") - || model.equals("SM-G9308") - || model.equals("SM-G930F") - || model.equals("SM-G930K") - || model.equals("SM-G930L") - || model.equals("SM-G930P") - || model.equals("SM-G930R4") - || model.equals("SM-G930R6") - || model.equals("SM-G930R7") - || model.equals("SM-G930S") - || model.equals("SM-G930T") - || model.equals("SM-G930T1") - || model.equals("SM-G930U") - || model.equals("SM-G930V") - || model.equals("SM-G930VC") - || model.equals("SM-G930VL") - || model.equals("SM-G930W8") - || model.equals("SM-G930X")))) { - return "Galaxy S7"; - } - if ((codename != null && (codename.equals("SC-02H") - || codename.equals("SCV33") - || codename.equals("hero2lte") - || codename.equals("hero2ltebmc") - || codename.equals("hero2ltektt") - || codename.equals("hero2ltelgt") - || codename.equals("hero2lteskt") - || codename.equals("hero2qlteatt") - || codename.equals("hero2qltecctvzw") - || codename.equals("hero2qltechn") - || codename.equals("hero2qltespr") - || codename.equals("hero2qltetmo") - || codename.equals("hero2qlteue") - || codename.equals("hero2qlteusc") - || codename.equals("hero2qltevzw"))) - || (model != null && (model.equals("SAMSUNG-SM-G935A") - || model.equals("SC-02H") - || model.equals("SCV33") - || model.equals("SM-G9350") - || model.equals("SM-G935F") - || model.equals("SM-G935K") - || model.equals("SM-G935L") - || model.equals("SM-G935P") - || model.equals("SM-G935R4") - || model.equals("SM-G935S") - || model.equals("SM-G935T") - || model.equals("SM-G935U") - || model.equals("SM-G935V") - || model.equals("SM-G935VC") - || model.equals("SM-G935W8") - || model.equals("SM-G935X")))) { - return "Galaxy S7 Edge"; - } - if ((codename != null && (codename.equals("SC-02J") - || codename.equals("SCV36") - || codename.equals("dreamlte") - || codename.equals("dreamlteks") - || codename.equals("dreamqltecan") - || codename.equals("dreamqltechn") - || codename.equals("dreamqltecmcc") - || codename.equals("dreamqltesq") - || codename.equals("dreamqlteue"))) - || (model != null && (model.equals("SC-02J") - || model.equals("SCV36") - || model.equals("SM-G9500") - || model.equals("SM-G9508") - || model.equals("SM-G950F") - || model.equals("SM-G950N") - || model.equals("SM-G950U") - || model.equals("SM-G950U1") - || model.equals("SM-G950W")))) { - return "Galaxy S8"; - } - if ((codename != null && (codename.equals("SC-03J") - || codename.equals("SCV35") - || codename.equals("dream2lte") - || codename.equals("dream2lteks") - || codename.equals("dream2qltecan") - || codename.equals("dream2qltechn") - || codename.equals("dream2qltesq") - || codename.equals("dream2qlteue"))) - || (model != null && (model.equals("SC-03J") - || model.equals("SCV35") - || model.equals("SM-G9550") - || model.equals("SM-G955F") - || model.equals("SM-G955N") - || model.equals("SM-G955U") - || model.equals("SM-G955U1") - || model.equals("SM-G955W")))) { - return "Galaxy S8+"; - } - if ((codename != null && (codename.equals("starlte") - || codename.equals("starlteks") - || codename.equals("starqltechn") - || codename.equals("starqltecmcc") - || codename.equals("starqltecs") - || codename.equals("starqltesq") - || codename.equals("starqlteue"))) - || (model != null && (model.equals("SM-G9600") - || model.equals("SM-G9608") - || model.equals("SM-G960F") - || model.equals("SM-G960N") - || model.equals("SM-G960U") - || model.equals("SM-G960U1") - || model.equals("SM-G960W")))) { - return "Galaxy S9"; - } - if ((codename != null && (codename.equals("star2lte") - || codename.equals("star2lteks") - || codename.equals("star2qltechn") - || codename.equals("star2qltecs") - || codename.equals("star2qltesq") - || codename.equals("star2qlteue"))) - || (model != null && (model.equals("SM-G9650") - || model.equals("SM-G965F") - || model.equals("SM-G965N") - || model.equals("SM-G965U") - || model.equals("SM-G965U1") - || model.equals("SM-G965W")))) { - return "Galaxy S9+"; - } - if ((codename != null && (codename.equals("GT-P7500") - || codename.equals("GT-P7500D") - || codename.equals("GT-P7503") - || codename.equals("GT-P7510") - || codename.equals("SC-01D") - || codename.equals("SCH-I905") - || codename.equals("SGH-T859") - || codename.equals("SHW-M300W") - || codename.equals("SHW-M380K") - || codename.equals("SHW-M380S") - || codename.equals("SHW-M380W"))) - || (model != null && (model.equals("GT-P7500") - || model.equals("GT-P7500D") - || model.equals("GT-P7503") - || model.equals("GT-P7510") - || model.equals("SC-01D") - || model.equals("SCH-I905") - || model.equals("SGH-T859") - || model.equals("SHW-M300W") - || model.equals("SHW-M380K") - || model.equals("SHW-M380S") - || model.equals("SHW-M380W")))) { - return "Galaxy Tab 10.1"; - } - if ((codename != null && (codename.equals("GT-P6200") - || codename.equals("GT-P6200L") - || codename.equals("GT-P6201") - || codename.equals("GT-P6210") - || codename.equals("GT-P6211") - || codename.equals("SC-02D") - || codename.equals("SGH-T869") - || codename.equals("SHW-M430W"))) - || (model != null && (model.equals("GT-P6200") - || model.equals("GT-P6200L") - || model.equals("GT-P6201") - || model.equals("GT-P6210") - || model.equals("GT-P6211") - || model.equals("SC-02D") - || model.equals("SGH-T869") - || model.equals("SHW-M430W")))) { - return "Galaxy Tab 7.0 Plus"; - } - if ((codename != null && (codename.equals("gteslteatt") - || codename.equals("gtesltebmc") - || codename.equals("gtesltelgt") - || codename.equals("gteslteskt") - || codename.equals("gtesltetmo") - || codename.equals("gtesltetw") - || codename.equals("gtesltevzw") - || codename.equals("gtesqltespr") - || codename.equals("gtesqlteusc"))) - || (model != null && (model.equals("SAMSUNG-SM-T377A") - || model.equals("SM-T375L") - || model.equals("SM-T375S") - || model.equals("SM-T3777") - || model.equals("SM-T377P") - || model.equals("SM-T377R4") - || model.equals("SM-T377T") - || model.equals("SM-T377V") - || model.equals("SM-T377W")))) { - return "Galaxy Tab E 8.0"; - } - if ((codename != null && (codename.equals("gtel3g") - || codename.equals("gtelltevzw") - || codename.equals("gtelwifi") - || codename.equals("gtelwifichn") - || codename.equals("gtelwifiue"))) - || (model != null && (model.equals("SM-T560") - || model.equals("SM-T560NU") - || model.equals("SM-T561") - || model.equals("SM-T561M") - || model.equals("SM-T561Y") - || model.equals("SM-T562") - || model.equals("SM-T567V")))) { - return "Galaxy Tab E 9.6"; - } - if ((codename != null && (codename.equals("403SC") - || codename.equals("degas2wifi") - || codename.equals("degas2wifibmwchn") - || codename.equals("degas3g") - || codename.equals("degaslte") - || codename.equals("degasltespr") - || codename.equals("degasltevzw") - || codename.equals("degasvelte") - || codename.equals("degasveltechn") - || codename.equals("degaswifi") - || codename.equals("degaswifibmwzc") - || codename.equals("degaswifidtv") - || codename.equals("degaswifiopenbnn") - || codename.equals("degaswifiue"))) - || (model != null && (model.equals("403SC") - || model.equals("SM-T230") - || model.equals("SM-T230NT") - || model.equals("SM-T230NU") - || model.equals("SM-T230NW") - || model.equals("SM-T230NY") - || model.equals("SM-T230X") - || model.equals("SM-T231") - || model.equals("SM-T232") - || model.equals("SM-T235") - || model.equals("SM-T235Y") - || model.equals("SM-T237P") - || model.equals("SM-T237V") - || model.equals("SM-T239") - || model.equals("SM-T2397") - || model.equals("SM-T239C") - || model.equals("SM-T239M")))) { - return "Galaxy Tab4 7.0"; - } - if ((codename != null && (codename.equals("gvlte") - || codename.equals("gvlteatt") - || codename.equals("gvltevzw") - || codename.equals("gvltexsp") - || codename.equals("gvwifijpn") - || codename.equals("gvwifiue"))) - || (model != null && (model.equals("SAMSUNG-SM-T677A") - || model.equals("SM-T670") - || model.equals("SM-T677") - || model.equals("SM-T677V")))) { - return "Galaxy View"; - } - if ((codename != null && codename.equals("manta"))) { - return "Nexus 10"; - } - // ---------------------------------------------------------------------------- - // Sony - if ((codename != null && (codename.equals("D2104") - || codename.equals("D2105"))) - || (model != null && (model.equals("D2104") - || model.equals("D2105")))) { - return "Xperia E1 dual"; - } - if ((codename != null && (codename.equals("D2202") - || codename.equals("D2203") - || codename.equals("D2206") - || codename.equals("D2243"))) - || (model != null && (model.equals("D2202") - || model.equals("D2203") - || model.equals("D2206") - || model.equals("D2243")))) { - return "Xperia E3"; - } - if ((codename != null && (codename.equals("E5603") - || codename.equals("E5606") - || codename.equals("E5653"))) - || (model != null && (model.equals("E5603") - || model.equals("E5606") - || model.equals("E5653")))) { - return "Xperia M5"; - } - if ((codename != null && (codename.equals("E5633") - || codename.equals("E5643") - || codename.equals("E5663"))) - || (model != null && (model.equals("E5633") - || model.equals("E5643") - || model.equals("E5663")))) { - return "Xperia M5 Dual"; - } - if ((codename != null && codename.equals("LT26i")) - || (model != null && model.equals("LT26i"))) { - return "Xperia S"; - } - if ((codename != null && (codename.equals("D5303") - || codename.equals("D5306") - || codename.equals("D5316") - || codename.equals("D5316N") - || codename.equals("D5322"))) - || (model != null && (model.equals("D5303") - || model.equals("D5306") - || model.equals("D5316") - || model.equals("D5316N") - || model.equals("D5322")))) { - return "Xperia T2 Ultra"; - } - if ((codename != null && (codename.equals("txs03"))) - || (model != null && (model.equals("SGPT12") - || model.equals("SGPT13")))) { - return "Xperia Tablet S"; - } - if ((codename != null && (codename.equals("SGP311") - || codename.equals("SGP312") - || codename.equals("SGP321") - || codename.equals("SGP351"))) - || (model != null && (model.equals("SGP311") - || model.equals("SGP312") - || model.equals("SGP321") - || model.equals("SGP351")))) { - return "Xperia Tablet Z"; - } - if ((codename != null && (codename.equals("D6502") - || codename.equals("D6503") - || codename.equals("D6543") - || codename.equals("SO-03F"))) - || (model != null && (model.equals("D6502") - || model.equals("D6503") - || model.equals("D6543") - || model.equals("SO-03F")))) { - return "Xperia Z2"; - } - if ((codename != null && (codename.equals("401SO") - || codename.equals("D6603") - || codename.equals("D6616") - || codename.equals("D6643") - || codename.equals("D6646") - || codename.equals("D6653") - || codename.equals("SO-01G") - || codename.equals("SOL26") - || codename.equals("leo"))) - || (model != null && (model.equals("401SO") - || model.equals("D6603") - || model.equals("D6616") - || model.equals("D6643") - || model.equals("D6646") - || model.equals("D6653") - || model.equals("SO-01G") - || model.equals("SOL26")))) { - return "Xperia Z3"; - } - if ((codename != null && (codename.equals("402SO") - || codename.equals("SO-03G") - || codename.equals("SOV31"))) - || (model != null && (model.equals("402SO") - || model.equals("SO-03G") - || model.equals("SOV31")))) { - return "Xperia Z4"; - } - if ((codename != null && (codename.equals("E5803") - || codename.equals("E5823") - || codename.equals("SO-02H"))) - || (model != null && (model.equals("E5803") - || model.equals("E5823") - || model.equals("SO-02H")))) { - return "Xperia Z5 Compact"; - } - // ---------------------------------------------------------------------------- - // Sony Ericsson - if ((codename != null && (codename.equals("LT26i") - || codename.equals("SO-02D"))) - || (model != null && (model.equals("LT26i") - || model.equals("SO-02D")))) { - return "Xperia S"; - } - if ((codename != null && (codename.equals("SGP311") - || codename.equals("SGP321") - || codename.equals("SGP341") - || codename.equals("SO-03E"))) - || (model != null && (model.equals("SGP311") - || model.equals("SGP321") - || model.equals("SGP341") - || model.equals("SO-03E")))) { - return "Xperia Tablet Z"; - } - return fallback; - } - - /** - * Get the {@link DeviceInfo} for the current device. Do not run on the UI thread, as this may - * download JSON to retrieve the {@link DeviceInfo}. JSON is only downloaded once and then - * stored to {@link SharedPreferences}. - * - * @param context - * the application context. - * @return {@link DeviceInfo} for the current device. - */ - @WorkerThread - public static DeviceInfo getDeviceInfo(Context context) { - return getDeviceInfo(context.getApplicationContext(), Build.DEVICE, Build.MODEL); - } - - /** - * Get the {@link DeviceInfo} for the current device. Do not run on the UI thread, as this may - * download JSON to retrieve the {@link DeviceInfo}. JSON is only downloaded once and then - * stored to {@link SharedPreferences}. - * - * @param context - * the application context. - * @param codename - * the codename of the device - * @return {@link DeviceInfo} for the current device. - */ - @WorkerThread - public static DeviceInfo getDeviceInfo(Context context, String codename) { - return getDeviceInfo(context, codename, null); - } - - /** - * Get the {@link DeviceInfo} for the current device. Do not run on the UI thread, as this may - * download JSON to retrieve the {@link DeviceInfo}. JSON is only downloaded once and then - * stored to {@link SharedPreferences}. - * - * @param context - * the application context. - * @param codename - * the codename of the device - * @param model - * the model of the device - * @return {@link DeviceInfo} for the current device. - */ - @WorkerThread - public static DeviceInfo getDeviceInfo(Context context, String codename, String model) { - SharedPreferences prefs = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); - String key = String.format("%s:%s", codename, model); - String savedJson = prefs.getString(key, null); - if (savedJson != null) { - try { - return new DeviceInfo(new JSONObject(savedJson)); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - // check if we have an internet connection - int ret = context.checkCallingOrSelfPermission(Manifest.permission.ACCESS_NETWORK_STATE); - boolean isConnectedToNetwork = false; - if (ret == PackageManager.PERMISSION_GRANTED) { - ConnectivityManager connMgr = (ConnectivityManager) - context.getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); - if (networkInfo != null && networkInfo.isConnected()) { - isConnectedToNetwork = true; - } - } else { - // assume we are connected. - isConnectedToNetwork = true; - } - - if (isConnectedToNetwork) { - try { - // Get the device name from the generated JSON files created from Google's device list. - String url = String.format(DEVICE_JSON_URL, codename.toLowerCase(Locale.ENGLISH)); - String jsonString = downloadJson(url); - JSONArray jsonArray = new JSONArray(jsonString); - for (int i = 0, len = jsonArray.length(); i < len; i++) { - JSONObject json = jsonArray.getJSONObject(i); - DeviceInfo info = new DeviceInfo(json); - if ((codename.equalsIgnoreCase(info.codename) && model == null) - || codename.equalsIgnoreCase(info.codename) && model.equalsIgnoreCase(info.model)) { - // Save to SharedPreferences so we don't need to make another request. - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(key, json.toString()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { - editor.apply(); - } else { - editor.commit(); - } - return info; - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (codename.equals(Build.DEVICE) && model.equals(Build.MODEL)) { - return new DeviceInfo(Build.MANUFACTURER, getDeviceName(), codename, model); // current device - } - - return new DeviceInfo(null, null, codename, model); // unknown device - } - - /** - *

Capitalizes getAllProcesses the whitespace separated words in a String. Only the first - * letter of each word is changed.

- * - * Whitespace is defined by {@link Character#isWhitespace(char)}. - * - * @param str - * the String to capitalize - * @return capitalized The capitalized String - */ - private static String capitalize(String str) { - if (TextUtils.isEmpty(str)) { - return str; - } - char[] arr = str.toCharArray(); - boolean capitalizeNext = true; - String phrase = ""; - for (char c : arr) { - if (capitalizeNext && Character.isLetter(c)) { - phrase += Character.toUpperCase(c); - capitalizeNext = false; - continue; - } else if (Character.isWhitespace(c)) { - capitalizeNext = true; - } - phrase += c; - } - return phrase; - } - - /** Download URL to String */ - @WorkerThread - private static String downloadJson(String myurl) throws IOException { - StringBuilder sb = new StringBuilder(); - BufferedReader reader = null; - try { - URL url = new URL(myurl); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setReadTimeout(10000); - conn.setConnectTimeout(15000); - conn.setRequestMethod("GET"); - conn.setDoInput(true); - conn.connect(); - if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); - String line; - while ((line = reader.readLine()) != null) { - sb.append(line).append('\n'); - } - } - return sb.toString(); - } finally { - if (reader != null) { - reader.close(); - } - } - } - - public static final class Request { - - final Context context; - final Handler handler; - String codename; - String model; - - private Request(Context ctx) { - context = ctx; - handler = new Handler(ctx.getMainLooper()); - } - - /** - * Set the device codename to query. You should also set the model. - * - * @param codename - * the value of the system property "ro.product.device" - * @return This Request object to allow for chaining of calls to set methods. - * @see Build#DEVICE - */ - public Request setCodename(String codename) { - this.codename = codename; - return this; - } - - /** - * Set the device model to query. You should also set the codename. - * - * @param model - * the value of the system property "ro.product.model" - * @return This Request object to allow for chaining of calls to set methods. - * @see Build#MODEL - */ - public Request setModel(String model) { - this.model = model; - return this; - } - - /** - * Download information about the device. This saves the results in shared-preferences so - * future requests will not need a network connection. - * - * @param callback - * the callback to retrieve the {@link DeviceInfo} - */ - public void request(Callback callback) { - if (codename == null && model == null) { - codename = Build.DEVICE; - model = Build.MODEL; - } - GetDeviceRunnable runnable = new GetDeviceRunnable(callback); - if (Looper.myLooper() == Looper.getMainLooper()) { - new Thread(runnable).start(); - } else { - runnable.run(); // already running in background thread. - } - } - - private final class GetDeviceRunnable implements Runnable { - - final Callback callback; - DeviceInfo deviceInfo; - Exception error; - - public GetDeviceRunnable(Callback callback) { - this.callback = callback; - } - - @Override public void run() { - try { - deviceInfo = getDeviceInfo(context, codename, model); - } catch (Exception e) { - error = e; - } - handler.post(new Runnable() { - - @Override public void run() { - callback.onFinished(deviceInfo, error); - } - }); - } - } - - } - - /** - * Callback which is invoked when the {@link DeviceInfo} is finished loading. - */ - public interface Callback { - - /** - * Callback to get the device info. This is run on the UI thread. - * - * @param info - * the requested {@link DeviceInfo} - * @param error - * {@code null} if nothing went wrong. - */ - void onFinished(DeviceInfo info, Exception error); - } - - /** - * Device information based on - * Google's maintained list. - */ - public static final class DeviceInfo { - - /** Retail branding */ - public final String manufacturer; - - /** Marketing name */ - public final String marketName; - - /** the value of the system property "ro.product.device" */ - public final String codename; - - /** the value of the system property "ro.product.model" */ - public final String model; - - public DeviceInfo(String manufacturer, String marketName, String codename, String model) { - this.manufacturer = manufacturer; - this.marketName = marketName; - this.codename = codename; - this.model = model; - } - - private DeviceInfo(JSONObject jsonObject) throws JSONException { - manufacturer = jsonObject.getString("manufacturer"); - marketName = jsonObject.getString("market_name"); - codename = jsonObject.getString("codename"); - model = jsonObject.getString("model"); - } - - /** - * @return the consumer friendly name of the device. - */ - public String getName() { - if (!TextUtils.isEmpty(marketName)) { - return marketName; - } - return capitalize(model); - } - } - -} - +package com.tangem.wallet; + +/** + * Created by Ilia on 20.04.2018. + */ +/* + * Copyright (C) 2017 Jared Rummler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + import android.Manifest; + import android.content.Context; + import android.content.SharedPreferences; + import android.content.pm.PackageManager; + import android.net.ConnectivityManager; + import android.net.NetworkInfo; + import android.os.Build; + import android.os.Handler; + import android.os.Looper; + import android.support.annotation.WorkerThread; + import android.text.TextUtils; + import java.io.BufferedReader; + import java.io.IOException; + import java.io.InputStreamReader; + import java.net.HttpURLConnection; + import java.net.URL; + import java.util.Locale; + import org.json.JSONArray; + import org.json.JSONException; + import org.json.JSONObject; + +// @formatter:off +/** + *

Get the consumer friendly name of an Android device.

+ * + *

On many popular devices the market name of the device is not available. For example, on the + * Samsung Galaxy S6 the value of {@link Build#MODEL} could be "SM-G920F", "SM-G920I", "SM-G920W8", + * etc.

+ * + *

See the usages below to get the consumer friends name of a device:

+ * + *

Get the name of the current device:

+ * + *
+ * String deviceName = DeviceName.getDeviceName();
+ * 
+ * + *

The above code will get the correct device name for the top 600 Android devices. If the + * device is unrecognized, then Build.MODEL is returned.

+ * + *

Get the name of a device using the device's codename:

+ * + *
+ * // Retruns "Moto X Style"
+ * DeviceName.getDeviceName("clark", "Unknown device");
+ * 
+ * + *

Get information about the device:

+ * + *
+ * DeviceName.with(context).request(new DeviceName.Callback() {
+ *
+ *   @Override public void onFinished(DeviceName.DeviceInfo info, Exception error) {
+ *     String manufacturer = info.manufacturer;  // "Samsung"
+ *     String name = info.marketName;            // "Galaxy S6 Edge"
+ *     String model = info.model;                // "SM-G925I"
+ *     String codename = info.codename;          // "zerolte"
+ *     String deviceName = info.getName();       // "Galaxy S6 Edge"
+ *     // FYI: We are on the UI thread.
+ *   }
+ * });
+ * 
+ * + *

The above code loads JSON from a generated list of device names based on Google's maintained + * list. It will be up-to-date with Google's supported device list so that you will get the correct + * name for new or unknown devices. This supports over 10,000 devices.

+ * + *

This will only make a network call once. The value is saved to SharedPreferences for future + * calls.

+ */ + +public class DeviceName { + + // @formatter:on + + // JSON which is derived from Google's PDF document which contains all devices on Google Play. + // To get the URL to the JSON file which contains information about the device name: + // String url = String.format(DEVICE_JSON_URL, Build.DEVICE); + private static final String DEVICE_JSON_URL = + "https://raw.githubusercontent.com/jaredrummler/AndroidDeviceNames/master/json/devices/%s.json"; + + // Preference filename for storing device info so we don't need to download it again. + private static final String SHARED_PREF_NAME = "device_names"; + + /** + * Create a new request to get information about a device. + * + * @param context + * the application context + * @return a new Request instance. + */ + public static Request with(Context context) { + return new Request(context.getApplicationContext()); + } + + /** + * Get the consumer friendly name of the device. + * + * @return the market name of the current device. + * @see #getDeviceName(String, String) + */ + public static String getDeviceName() { + return getDeviceName(Build.DEVICE, Build.MODEL, capitalize(Build.MODEL)); + } + + /** + * Get the consumer friendly name of a device. + * + * @param codename + * the value of the system property "ro.product.device" ({@link Build#DEVICE}) + * or + * the value of the system property "ro.product.model" ({@link Build#MODEL}) + * @param fallback + * the fallback name if the device is unknown. Usually the value of the system property + * "ro.product.model" ({@link Build#MODEL}) + * @return the market name of a device or {@code fallback} if the device is unknown. + */ + public static String getDeviceName(String codename, String fallback) { + return getDeviceName(codename, codename, fallback); + } + + /** + * Get the consumer friendly name of a device. + * + * @param codename + * the value of the system property "ro.product.device" ({@link Build#DEVICE}). + * @param model + * the value of the system property "ro.product.model" ({@link Build#MODEL}). + * @param fallback + * the fallback name if the device is unknown. Usually the value of the system property + * "ro.product.model" ({@link Build#MODEL}) + * @return the market name of a device or {@code fallback} if the device is unknown. + */ + public static String getDeviceName(String codename, String model, String fallback) { + // ---------------------------------------------------------------------------- + // Acer + if ((codename != null && codename.equals("acer_S57")) + || (model != null && model.equals("S57"))) { + return "Liquid Jade Z"; + } + if ((codename != null && codename.equals("acer_t08")) + || (model != null && model.equals("T08"))) { + return "Liquid Zest Plus"; + } + // ---------------------------------------------------------------------------- + // Asus + if ((codename != null && (codename.equals("grouper") + || codename.equals("tilapia")))) { + return "Nexus 7 (2012)"; + } + if ((codename != null && (codename.equals("deb") + || codename.equals("flo")))) { + return "Nexus 7 (2013)"; + } + // ---------------------------------------------------------------------------- + // Google + if ((codename != null && codename.equals("sailfish"))) { + return "Pixel"; + } + if ((codename != null && codename.equals("walleye"))) { + return "Pixel 2"; + } + if ((codename != null && codename.equals("taimen"))) { + return "Pixel 2 XL"; + } + if ((codename != null && codename.equals("dragon"))) { + return "Pixel C"; + } + if ((codename != null && codename.equals("marlin"))) { + return "Pixel XL"; + } + // ---------------------------------------------------------------------------- + // HTC + if ((codename != null && codename.equals("flounder"))) { + return "Nexus 9"; + } + // ---------------------------------------------------------------------------- + // Huawei + if ((codename != null && (codename.equals("HWBND-H"))) + || (model != null && (model.equals("BND-L21") + || model.equals("BND-L24")))) { + return "Honor 7X"; + } + if (model != null && model.contains("WAS-LX")) { + return "P10 lite"; + } + if ((codename != null && codename.equals("HWBKL")) + || (model != null && model.equals("BKL-L09"))) { + return "Honor View 10"; + } + if ((codename != null && (codename.equals("HWALP"))) + || (model != null && (model.equals("ALP-AL00") + || model.equals("ALP-L09") + || model.equals("ALP-L29") + || model.equals("ALP-TL00")))) { + return "Mate 10"; + } + if ((codename != null && (codename.equals("HWMHA"))) + || (model != null && (model.equals("MHA-AL00") + || model.equals("MHA-L09") + || model.equals("MHA-L29") + || model.equals("MHA-TL00")))) { + return "Mate 9"; + } + if ((codename != null && codename.equals("angler"))) { + return "Nexus 6P"; + } + // ---------------------------------------------------------------------------- + // LGE + if ((codename != null && (codename.equals("g2"))) + || (model != null && (model.equals("LG-D800") + || model.equals("LG-D801") + || model.equals("LG-D802") + || model.equals("LG-D802T") + || model.equals("LG-D802TR") + || model.equals("LG-D803") + || model.equals("LG-D805") + || model.equals("LG-D806") + || model.equals("LG-F320K") + || model.equals("LG-F320L") + || model.equals("LG-F320S") + || model.equals("LG-LS980") + || model.equals("VS980 4G")))) { + return "LG G2"; + } + if ((codename != null && (codename.equals("g3"))) + || (model != null && (model.equals("AS985") + || model.equals("LG-AS990") + || model.equals("LG-D850") + || model.equals("LG-D851") + || model.equals("LG-D852") + || model.equals("LG-D852G") + || model.equals("LG-D855") + || model.equals("LG-D856") + || model.equals("LG-D857") + || model.equals("LG-D858") + || model.equals("LG-D858HK") + || model.equals("LG-D859") + || model.equals("LG-F400K") + || model.equals("LG-F400L") + || model.equals("LG-F400S") + || model.equals("LGL24") + || model.equals("LGLS990") + || model.equals("LGUS990") + || model.equals("LGV31") + || model.equals("VS985 4G")))) { + return "LG G3"; + } + if ((codename != null && (codename.equals("p1"))) + || (model != null && (model.equals("AS986") + || model.equals("LG-AS811") + || model.equals("LG-AS991") + || model.equals("LG-F500K") + || model.equals("LG-F500L") + || model.equals("LG-F500S") + || model.equals("LG-H810") + || model.equals("LG-H811") + || model.equals("LG-H812") + || model.equals("LG-H815") + || model.equals("LG-H818") + || model.equals("LG-H819") + || model.equals("LGLS991") + || model.equals("LGUS991") + || model.equals("LGV32") + || model.equals("VS986")))) { + return "LG G4"; + } + if ((codename != null && (codename.equals("h1"))) + || (model != null && (model.equals("LG-F700K") + || model.equals("LG-F700L") + || model.equals("LG-F700S") + || model.equals("LG-H820") + || model.equals("LG-H820PR") + || model.equals("LG-H830") + || model.equals("LG-H831") + || model.equals("LG-H850") + || model.equals("LG-H858") + || model.equals("LG-H860") + || model.equals("LG-H868") + || model.equals("LGAS992") + || model.equals("LGLS992") + || model.equals("LGUS992") + || model.equals("RS988") + || model.equals("VS987")))) { + return "LG G5"; + } + if ((codename != null && (codename.equals("lucye"))) + || (model != null && (model.equals("LG-AS993") + || model.equals("LG-H870") + || model.equals("LG-H870AR") + || model.equals("LG-H870DS") + || model.equals("LG-H870I") + || model.equals("LG-H870S") + || model.equals("LG-H871") + || model.equals("LG-H872") + || model.equals("LG-H872PR") + || model.equals("LG-H873") + || model.equals("LG-LS993") + || model.equals("LGM-G600K") + || model.equals("LGM-G600L") + || model.equals("LGM-G600S") + || model.equals("LGUS997") + || model.equals("VS988")))) { + return "LG G6"; + } + if ((codename != null && codename.equals("mako"))) { + return "Nexus 4"; + } + if ((codename != null && codename.equals("hammerhead"))) { + return "Nexus 5"; + } + if ((codename != null && codename.equals("bullhead"))) { + return "Nexus 5X"; + } + // ---------------------------------------------------------------------------- + // Motorola + if ((codename != null && codename.equals("shamu"))) { + return "Nexus 6"; + } + // ---------------------------------------------------------------------------- + // OnePlus + if ((codename != null && codename.equals("OnePlus3")) + || (model != null && model.equals("ONEPLUS A3000"))) { + return "OnePlus3"; + } + if ((codename != null && codename.equals("OnePlus3T")) + || (model != null && model.equals("ONEPLUS A3000"))) { + return "OnePlus3T"; + } + if ((codename != null && codename.equals("OnePlus5")) + || (model != null && model.equals("ONEPLUS A5000"))) { + return "OnePlus5"; + } + if ((codename != null && codename.equals("OnePlus5T")) + || (model != null && model.equals("ONEPLUS A5010"))) { + return "OnePlus5T"; + } + // ---------------------------------------------------------------------------- + // Samsung + if ((codename != null && (codename.equals("a53g") + || codename.equals("a5lte") + || codename.equals("a5ltechn") + || codename.equals("a5ltectc") + || codename.equals("a5ltezh") + || codename.equals("a5ltezt") + || codename.equals("a5ulte") + || codename.equals("a5ultebmc") + || codename.equals("a5ultektt") + || codename.equals("a5ultelgt") + || codename.equals("a5ulteskt"))) + || (model != null && (model.equals("SM-A5000") + || model.equals("SM-A5009") + || model.equals("SM-A500F") + || model.equals("SM-A500F1") + || model.equals("SM-A500FU") + || model.equals("SM-A500G") + || model.equals("SM-A500H") + || model.equals("SM-A500K") + || model.equals("SM-A500L") + || model.equals("SM-A500M") + || model.equals("SM-A500S") + || model.equals("SM-A500W") + || model.equals("SM-A500X") + || model.equals("SM-A500XZ") + || model.equals("SM-A500Y") + || model.equals("SM-A500YZ")))) { + return "Galaxy A5"; + } + if ((codename != null && (codename.equals("vivaltods5m"))) + || (model != null && (model.equals("SM-G313HU") + || model.equals("SM-G313HY") + || model.equals("SM-G313M") + || model.equals("SM-G313MY")))) { + return "Galaxy Ace 4"; + } + if ((codename != null && (codename.equals("GT-S6352") + || codename.equals("GT-S6802") + || codename.equals("GT-S6802B") + || codename.equals("SCH-I579") + || codename.equals("SCH-I589") + || codename.equals("SCH-i579") + || codename.equals("SCH-i589"))) + || (model != null && (model.equals("GT-S6352") + || model.equals("GT-S6802") + || model.equals("GT-S6802B") + || model.equals("SCH-I589") + || model.equals("SCH-i579") + || model.equals("SCH-i589")))) { + return "Galaxy Ace Duos"; + } + if ((codename != null && (codename.equals("GT-S7500") + || codename.equals("GT-S7500L") + || codename.equals("GT-S7500T") + || codename.equals("GT-S7500W") + || codename.equals("GT-S7508"))) + || (model != null && (model.equals("GT-S7500") + || model.equals("GT-S7500L") + || model.equals("GT-S7500T") + || model.equals("GT-S7500W") + || model.equals("GT-S7508")))) { + return "Galaxy Ace Plus"; + } + if ((codename != null && (codename.equals("heat3gtfnvzw") + || codename.equals("heatnfc3g") + || codename.equals("heatqlte"))) + || (model != null && (model.equals("SM-G310HN") + || model.equals("SM-G357FZ") + || model.equals("SM-S765C") + || model.equals("SM-S766C")))) { + return "Galaxy Ace Style"; + } + if ((codename != null && (codename.equals("vivalto3g") + || codename.equals("vivalto3mve3g") + || codename.equals("vivalto5mve3g") + || codename.equals("vivaltolte") + || codename.equals("vivaltonfc3g"))) + || (model != null && (model.equals("SM-G313F") + || model.equals("SM-G313HN") + || model.equals("SM-G313ML") + || model.equals("SM-G313MU") + || model.equals("SM-G316H") + || model.equals("SM-G316HU") + || model.equals("SM-G316M") + || model.equals("SM-G316MY")))) { + return "Galaxy Ace4"; + } + if ((codename != null && (codename.equals("core33g") + || codename.equals("coreprimelte") + || codename.equals("coreprimelteaio") + || codename.equals("coreprimeltelra") + || codename.equals("coreprimeltespr") + || codename.equals("coreprimeltetfnvzw") + || codename.equals("coreprimeltevzw") + || codename.equals("coreprimeve3g") + || codename.equals("coreprimevelte") + || codename.equals("cprimeltemtr") + || codename.equals("cprimeltetmo") + || codename.equals("rossalte") + || codename.equals("rossaltectc") + || codename.equals("rossaltexsa"))) + || (model != null && (model.equals("SAMSUNG-SM-G360AZ") + || model.equals("SM-G3606") + || model.equals("SM-G3608") + || model.equals("SM-G3609") + || model.equals("SM-G360F") + || model.equals("SM-G360FY") + || model.equals("SM-G360GY") + || model.equals("SM-G360H") + || model.equals("SM-G360HU") + || model.equals("SM-G360M") + || model.equals("SM-G360P") + || model.equals("SM-G360R6") + || model.equals("SM-G360T") + || model.equals("SM-G360T1") + || model.equals("SM-G360V") + || model.equals("SM-G361F") + || model.equals("SM-G361H") + || model.equals("SM-G361HU") + || model.equals("SM-G361M") + || model.equals("SM-S820L")))) { + return "Galaxy Core Prime"; + } + if ((codename != null && (codename.equals("kanas") + || codename.equals("kanas3g") + || codename.equals("kanas3gcmcc") + || codename.equals("kanas3gctc") + || codename.equals("kanas3gnfc"))) + || (model != null && (model.equals("SM-G3556D") + || model.equals("SM-G3558") + || model.equals("SM-G3559") + || model.equals("SM-G355H") + || model.equals("SM-G355HN") + || model.equals("SM-G355HQ") + || model.equals("SM-G355M")))) { + return "Galaxy Core2"; + } + if ((codename != null && (codename.equals("e53g") + || codename.equals("e5lte") + || codename.equals("e5ltetfnvzw") + || codename.equals("e5ltetw"))) + || (model != null && (model.equals("SM-E500F") + || model.equals("SM-E500H") + || model.equals("SM-E500M") + || model.equals("SM-E500YZ") + || model.equals("SM-S978L")))) { + return "Galaxy E5"; + } + if ((codename != null && (codename.equals("e73g") + || codename.equals("e7lte") + || codename.equals("e7ltechn") + || codename.equals("e7ltectc") + || codename.equals("e7ltehktw"))) + || (model != null && (model.equals("SM-E7000") + || model.equals("SM-E7009") + || model.equals("SM-E700F") + || model.equals("SM-E700H") + || model.equals("SM-E700M")))) { + return "Galaxy E7"; + } + if ((codename != null && (codename.equals("SCH-I629") + || codename.equals("nevis") + || codename.equals("nevis3g") + || codename.equals("nevis3gcmcc") + || codename.equals("nevisds") + || codename.equals("nevisnvess") + || codename.equals("nevisp") + || codename.equals("nevisvess") + || codename.equals("nevisw"))) + || (model != null && (model.equals("GT-S6790") + || model.equals("GT-S6790E") + || model.equals("GT-S6790L") + || model.equals("GT-S6790N") + || model.equals("GT-S6810") + || model.equals("GT-S6810B") + || model.equals("GT-S6810E") + || model.equals("GT-S6810L") + || model.equals("GT-S6810M") + || model.equals("GT-S6810P") + || model.equals("GT-S6812") + || model.equals("GT-S6812B") + || model.equals("GT-S6812C") + || model.equals("GT-S6812i") + || model.equals("GT-S6818") + || model.equals("GT-S6818V") + || model.equals("SCH-I629")))) { + return "Galaxy Fame"; + } + if ((codename != null && codename.equals("grandprimelteatt")) + || (model != null && model.equals("SAMSUNG-SM-G530A"))) { + return "Galaxy Go Prime"; + } + if ((codename != null && (codename.equals("baffinlite") + || codename.equals("baffinlitedtv") + || codename.equals("baffinq3g"))) + || (model != null && (model.equals("GT-I9060") + || model.equals("GT-I9060L") + || model.equals("GT-I9063T") + || model.equals("GT-I9082C") + || model.equals("GT-I9168") + || model.equals("GT-I9168I")))) { + return "Galaxy Grand Neo"; + } + if ((codename != null && (codename.equals("fortuna3g") + || codename.equals("fortuna3gdtv") + || codename.equals("fortunalte") + || codename.equals("fortunaltectc") + || codename.equals("fortunaltezh") + || codename.equals("fortunaltezt") + || codename.equals("fortunave3g") + || codename.equals("gprimelteacg") + || codename.equals("gprimeltecan") + || codename.equals("gprimeltemtr") + || codename.equals("gprimeltespr") + || codename.equals("gprimeltetfnvzw") + || codename.equals("gprimeltetmo") + || codename.equals("gprimelteusc") + || codename.equals("grandprimelte") + || codename.equals("grandprimelteaio") + || codename.equals("grandprimeve3g") + || codename.equals("grandprimeve3gdtv") + || codename.equals("grandprimevelte") + || codename.equals("grandprimevelteltn") + || codename.equals("grandprimeveltezt"))) + || (model != null && (model.equals("SAMSUNG-SM-G530AZ") + || model.equals("SM-G5306W") + || model.equals("SM-G5308W") + || model.equals("SM-G5309W") + || model.equals("SM-G530BT") + || model.equals("SM-G530F") + || model.equals("SM-G530FZ") + || model.equals("SM-G530H") + || model.equals("SM-G530M") + || model.equals("SM-G530MU") + || model.equals("SM-G530P") + || model.equals("SM-G530R4") + || model.equals("SM-G530R7") + || model.equals("SM-G530T") + || model.equals("SM-G530T1") + || model.equals("SM-G530W") + || model.equals("SM-G530Y") + || model.equals("SM-G531BT") + || model.equals("SM-G531F") + || model.equals("SM-G531H") + || model.equals("SM-G531M") + || model.equals("SM-G531Y") + || model.equals("SM-S920L") + || model.equals("gprimelteacg")))) { + return "Galaxy Grand Prime"; + } + if ((codename != null && (codename.equals("ms013g") + || codename.equals("ms013gdtv") + || codename.equals("ms013gss") + || codename.equals("ms01lte") + || codename.equals("ms01ltektt") + || codename.equals("ms01ltelgt") + || codename.equals("ms01lteskt"))) + || (model != null && (model.equals("SM-G710") + || model.equals("SM-G7102") + || model.equals("SM-G7102T") + || model.equals("SM-G7105") + || model.equals("SM-G7105H") + || model.equals("SM-G7105L") + || model.equals("SM-G7106") + || model.equals("SM-G7108") + || model.equals("SM-G7109") + || model.equals("SM-G710K") + || model.equals("SM-G710L") + || model.equals("SM-G710S")))) { + return "Galaxy Grand2"; + } + if ((codename != null && (codename.equals("j13g") + || codename.equals("j13gtfnvzw") + || codename.equals("j1lte") + || codename.equals("j1nlte") + || codename.equals("j1qltevzw") + || codename.equals("j1xlte") + || codename.equals("j1xlteaio") + || codename.equals("j1xlteatt") + || codename.equals("j1xltecan") + || codename.equals("j1xqltespr") + || codename.equals("j1xqltetfnvzw"))) + || (model != null && (model.equals("SAMSUNG-SM-J120A") + || model.equals("SAMSUNG-SM-J120AZ") + || model.equals("SM-J100F") + || model.equals("SM-J100FN") + || model.equals("SM-J100G") + || model.equals("SM-J100H") + || model.equals("SM-J100M") + || model.equals("SM-J100ML") + || model.equals("SM-J100MU") + || model.equals("SM-J100VPP") + || model.equals("SM-J100Y") + || model.equals("SM-J120F") + || model.equals("SM-J120FN") + || model.equals("SM-J120M") + || model.equals("SM-J120P") + || model.equals("SM-J120W") + || model.equals("SM-S120VL") + || model.equals("SM-S777C")))) { + return "Galaxy J1"; + } + if ((codename != null && (codename.equals("j1acelte") + || codename.equals("j1acelteltn") + || codename.equals("j1acevelte") + || codename.equals("j1pop3g"))) + || (model != null && (model.equals("SM-J110F") + || model.equals("SM-J110G") + || model.equals("SM-J110H") + || model.equals("SM-J110L") + || model.equals("SM-J110M") + || model.equals("SM-J111F") + || model.equals("SM-J111M")))) { + return "Galaxy J1 Ace"; + } + if ((codename != null && (codename.equals("j53g") + || codename.equals("j5lte") + || codename.equals("j5ltechn") + || codename.equals("j5ltekx") + || codename.equals("j5nlte") + || codename.equals("j5ylte"))) + || (model != null && (model.equals("SM-J5007") + || model.equals("SM-J5008") + || model.equals("SM-J500F") + || model.equals("SM-J500FN") + || model.equals("SM-J500G") + || model.equals("SM-J500H") + || model.equals("SM-J500M") + || model.equals("SM-J500N0") + || model.equals("SM-J500Y")))) { + return "Galaxy J5"; + } + if ((codename != null && (codename.equals("j75ltektt") + || codename.equals("j7e3g") + || codename.equals("j7elte") + || codename.equals("j7ltechn"))) + || (model != null && (model.equals("SM-J7008") + || model.equals("SM-J700F") + || model.equals("SM-J700H") + || model.equals("SM-J700K") + || model.equals("SM-J700M")))) { + return "Galaxy J7"; + } + if ((codename != null && (codename.equals("maguro") + || codename.equals("toro") + || codename.equals("toroplus"))) + || (model != null && (model.equals("Galaxy X")))) { + return "Galaxy Nexus"; + } + if ((codename != null && (codename.equals("lt033g") + || codename.equals("lt03ltektt") + || codename.equals("lt03ltelgt") + || codename.equals("lt03lteskt") + || codename.equals("p4notelte") + || codename.equals("p4noteltektt") + || codename.equals("p4noteltelgt") + || codename.equals("p4notelteskt") + || codename.equals("p4noteltespr") + || codename.equals("p4notelteusc") + || codename.equals("p4noteltevzw") + || codename.equals("p4noterf") + || codename.equals("p4noterfktt") + || codename.equals("p4notewifi") + || codename.equals("p4notewifi43241any") + || codename.equals("p4notewifiany") + || codename.equals("p4notewifiktt") + || codename.equals("p4notewifiww"))) + || (model != null && (model.equals("GT-N8000") + || model.equals("GT-N8005") + || model.equals("GT-N8010") + || model.equals("GT-N8013") + || model.equals("GT-N8020") + || model.equals("SCH-I925") + || model.equals("SCH-I925U") + || model.equals("SHV-E230K") + || model.equals("SHV-E230L") + || model.equals("SHV-E230S") + || model.equals("SHW-M480K") + || model.equals("SHW-M480W") + || model.equals("SHW-M485W") + || model.equals("SHW-M486W") + || model.equals("SM-P601") + || model.equals("SM-P602") + || model.equals("SM-P605K") + || model.equals("SM-P605L") + || model.equals("SM-P605S") + || model.equals("SPH-P600")))) { + return "Galaxy Note 10.1"; + } + if ((codename != null && (codename.equals("SC-01G") + || codename.equals("SCL24") + || codename.equals("tbeltektt") + || codename.equals("tbeltelgt") + || codename.equals("tbelteskt") + || codename.equals("tblte") + || codename.equals("tblteatt") + || codename.equals("tbltecan") + || codename.equals("tbltechn") + || codename.equals("tbltespr") + || codename.equals("tbltetmo") + || codename.equals("tblteusc") + || codename.equals("tbltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-N915A") + || model.equals("SC-01G") + || model.equals("SCL24") + || model.equals("SM-N9150") + || model.equals("SM-N915F") + || model.equals("SM-N915FY") + || model.equals("SM-N915G") + || model.equals("SM-N915K") + || model.equals("SM-N915L") + || model.equals("SM-N915P") + || model.equals("SM-N915R4") + || model.equals("SM-N915S") + || model.equals("SM-N915T") + || model.equals("SM-N915T3") + || model.equals("SM-N915V") + || model.equals("SM-N915W8") + || model.equals("SM-N915X")))) { + return "Galaxy Note Edge"; + } + if ((codename != null && (codename.equals("v1a3g") + || codename.equals("v1awifi") + || codename.equals("v1awifikx") + || codename.equals("viennalte") + || codename.equals("viennalteatt") + || codename.equals("viennaltekx") + || codename.equals("viennaltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-P907A") + || model.equals("SM-P900") + || model.equals("SM-P901") + || model.equals("SM-P905") + || model.equals("SM-P905F0") + || model.equals("SM-P905M") + || model.equals("SM-P905V")))) { + return "Galaxy Note Pro 12.2"; + } + if ((codename != null && (codename.equals("tre3caltektt") + || codename.equals("tre3caltelgt") + || codename.equals("tre3calteskt") + || codename.equals("tre3g") + || codename.equals("trelte") + || codename.equals("treltektt") + || codename.equals("treltelgt") + || codename.equals("trelteskt") + || codename.equals("trhplte") + || codename.equals("trlte") + || codename.equals("trlteatt") + || codename.equals("trltecan") + || codename.equals("trltechn") + || codename.equals("trltechnzh") + || codename.equals("trltespr") + || codename.equals("trltetmo") + || codename.equals("trlteusc") + || codename.equals("trltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-N910A") + || model.equals("SM-N9100") + || model.equals("SM-N9106W") + || model.equals("SM-N9108V") + || model.equals("SM-N9109W") + || model.equals("SM-N910C") + || model.equals("SM-N910F") + || model.equals("SM-N910G") + || model.equals("SM-N910H") + || model.equals("SM-N910K") + || model.equals("SM-N910L") + || model.equals("SM-N910P") + || model.equals("SM-N910R4") + || model.equals("SM-N910S") + || model.equals("SM-N910T") + || model.equals("SM-N910T2") + || model.equals("SM-N910T3") + || model.equals("SM-N910U") + || model.equals("SM-N910V") + || model.equals("SM-N910W8") + || model.equals("SM-N910X") + || model.equals("SM-N916K") + || model.equals("SM-N916L") + || model.equals("SM-N916S")))) { + return "Galaxy Note4"; + } + if ((codename != null && (codename.equals("noblelte") + || codename.equals("noblelteacg") + || codename.equals("noblelteatt") + || codename.equals("nobleltebmc") + || codename.equals("nobleltechn") + || codename.equals("nobleltecmcc") + || codename.equals("nobleltehk") + || codename.equals("nobleltektt") + || codename.equals("nobleltelgt") + || codename.equals("nobleltelra") + || codename.equals("noblelteskt") + || codename.equals("nobleltespr") + || codename.equals("nobleltetmo") + || codename.equals("noblelteusc") + || codename.equals("nobleltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-N920A") + || model.equals("SM-N9200") + || model.equals("SM-N9208") + || model.equals("SM-N920C") + || model.equals("SM-N920F") + || model.equals("SM-N920G") + || model.equals("SM-N920I") + || model.equals("SM-N920K") + || model.equals("SM-N920L") + || model.equals("SM-N920P") + || model.equals("SM-N920R4") + || model.equals("SM-N920R6") + || model.equals("SM-N920R7") + || model.equals("SM-N920S") + || model.equals("SM-N920T") + || model.equals("SM-N920V") + || model.equals("SM-N920W8") + || model.equals("SM-N920X")))) { + return "Galaxy Note5"; + } + if ((codename != null && (codename.equals("SC-01J") + || codename.equals("SCV34") + || codename.equals("gracelte") + || codename.equals("graceltektt") + || codename.equals("graceltelgt") + || codename.equals("gracelteskt") + || codename.equals("graceqlteacg") + || codename.equals("graceqlteatt") + || codename.equals("graceqltebmc") + || codename.equals("graceqltechn") + || codename.equals("graceqltedcm") + || codename.equals("graceqltelra") + || codename.equals("graceqltespr") + || codename.equals("graceqltetfnvzw") + || codename.equals("graceqltetmo") + || codename.equals("graceqlteue") + || codename.equals("graceqlteusc") + || codename.equals("graceqltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-N930A") + || model.equals("SC-01J") + || model.equals("SCV34") + || model.equals("SGH-N037") + || model.equals("SM-N9300") + || model.equals("SM-N930F") + || model.equals("SM-N930K") + || model.equals("SM-N930L") + || model.equals("SM-N930P") + || model.equals("SM-N930R4") + || model.equals("SM-N930R6") + || model.equals("SM-N930R7") + || model.equals("SM-N930S") + || model.equals("SM-N930T") + || model.equals("SM-N930U") + || model.equals("SM-N930V") + || model.equals("SM-N930VL") + || model.equals("SM-N930W8") + || model.equals("SM-N930X")))) { + return "Galaxy Note7"; + } + if ((codename != null && (codename.equals("SC-01K") + || codename.equals("SCV37") + || codename.equals("greatlte") + || codename.equals("greatlteks") + || codename.equals("greatqlte") + || codename.equals("greatqltechn") + || codename.equals("greatqltecmcc") + || codename.equals("greatqltecs") + || codename.equals("greatqlteue"))) + || (model != null && (model.equals("SC-01K") + || model.equals("SCV37") + || model.equals("SM-N9500") + || model.equals("SM-N9508") + || model.equals("SM-N950F") + || model.equals("SM-N950N") + || model.equals("SM-N950U") + || model.equals("SM-N950U1") + || model.equals("SM-N950W") + || model.equals("SM-N950XN")))) { + return "Galaxy Note8"; + } + if ((codename != null && (codename.equals("o5lte") + || codename.equals("o5ltechn") + || codename.equals("o5prolte") + || codename.equals("on5ltemtr") + || codename.equals("on5ltetfntmo") + || codename.equals("on5ltetmo"))) + || (model != null && (model.equals("SM-G5500") + || model.equals("SM-G550FY") + || model.equals("SM-G550T") + || model.equals("SM-G550T1") + || model.equals("SM-G550T2") + || model.equals("SM-S550TL")))) { + return "Galaxy On5"; + } + if ((codename != null && (codename.equals("o7lte") + || codename.equals("o7ltechn") + || codename.equals("on7elte"))) + || (model != null && (model.equals("SM-G6000") + || model.equals("SM-G600F") + || model.equals("SM-G600FY")))) { + return "Galaxy On7"; + } + if ((codename != null && (codename.equals("GT-I9000") + || codename.equals("GT-I9000B") + || codename.equals("GT-I9000M") + || codename.equals("GT-I9000T") + || codename.equals("GT-I9003") + || codename.equals("GT-I9003L") + || codename.equals("GT-I9008L") + || codename.equals("GT-I9010") + || codename.equals("GT-I9018") + || codename.equals("GT-I9050") + || codename.equals("SC-02B") + || codename.equals("SCH-I500") + || codename.equals("SCH-S950C") + || codename.equals("SCH-i909") + || codename.equals("SGH-I897") + || codename.equals("SGH-T959V") + || codename.equals("SGH-T959W") + || codename.equals("SHW-M110S") + || codename.equals("SHW-M190S") + || codename.equals("SPH-D700") + || codename.equals("loganlte"))) + || (model != null && (model.equals("GT-I9000") + || model.equals("GT-I9000B") + || model.equals("GT-I9000M") + || model.equals("GT-I9000T") + || model.equals("GT-I9003") + || model.equals("GT-I9003L") + || model.equals("GT-I9008L") + || model.equals("GT-I9010") + || model.equals("GT-I9018") + || model.equals("GT-I9050") + || model.equals("GT-S7275") + || model.equals("SAMSUNG-SGH-I897") + || model.equals("SC-02B") + || model.equals("SCH-I500") + || model.equals("SCH-S950C") + || model.equals("SCH-i909") + || model.equals("SGH-T959V") + || model.equals("SGH-T959W") + || model.equals("SHW-M110S") + || model.equals("SHW-M190S") + || model.equals("SPH-D700")))) { + return "Galaxy S"; + } + if ((codename != null && (codename.equals("kylechn") + || codename.equals("kyleopen") + || codename.equals("kyletdcmcc"))) + || (model != null && (model.equals("GT-S7562") + || model.equals("GT-S7568")))) { + return "Galaxy S Duos"; + } + if ((codename != null && (codename.equals("kyleprods"))) + || (model != null && (model.equals("GT-S7582") + || model.equals("GT-S7582L")))) { + return "Galaxy S Duos2"; + } + if ((codename != null && codename.equals("vivalto3gvn")) + || (model != null && model.equals("SM-G313HZ"))) { + return "Galaxy S Duos3"; + } + if ((codename != null && (codename.equals("SC-03E") + || codename.equals("c1att") + || codename.equals("c1ktt") + || codename.equals("c1lgt") + || codename.equals("c1skt") + || codename.equals("d2att") + || codename.equals("d2can") + || codename.equals("d2cri") + || codename.equals("d2dcm") + || codename.equals("d2lteMetroPCS") + || codename.equals("d2lterefreshspr") + || codename.equals("d2ltetmo") + || codename.equals("d2mtr") + || codename.equals("d2spi") + || codename.equals("d2spr") + || codename.equals("d2tfnspr") + || codename.equals("d2tfnvzw") + || codename.equals("d2tmo") + || codename.equals("d2usc") + || codename.equals("d2vmu") + || codename.equals("d2vzw") + || codename.equals("d2xar") + || codename.equals("m0") + || codename.equals("m0apt") + || codename.equals("m0chn") + || codename.equals("m0cmcc") + || codename.equals("m0ctc") + || codename.equals("m0ctcduos") + || codename.equals("m0skt") + || codename.equals("m3") + || codename.equals("m3dcm"))) + || (model != null && (model.equals("GT-I9300") + || model.equals("GT-I9300T") + || model.equals("GT-I9305") + || model.equals("GT-I9305N") + || model.equals("GT-I9305T") + || model.equals("GT-I9308") + || model.equals("Gravity") + || model.equals("GravityQuad") + || model.equals("SAMSUNG-SGH-I747") + || model.equals("SC-03E") + || model.equals("SC-06D") + || model.equals("SCH-I535") + || model.equals("SCH-I535PP") + || model.equals("SCH-I939") + || model.equals("SCH-I939D") + || model.equals("SCH-L710") + || model.equals("SCH-R530C") + || model.equals("SCH-R530M") + || model.equals("SCH-R530U") + || model.equals("SCH-R530X") + || model.equals("SCH-S960L") + || model.equals("SCH-S968C") + || model.equals("SGH-I747M") + || model.equals("SGH-I748") + || model.equals("SGH-T999") + || model.equals("SGH-T999L") + || model.equals("SGH-T999N") + || model.equals("SGH-T999V") + || model.equals("SHV-E210K") + || model.equals("SHV-E210L") + || model.equals("SHV-E210S") + || model.equals("SHW-M440S") + || model.equals("SPH-L710") + || model.equals("SPH-L710T")))) { + return "Galaxy S3"; + } + if ((codename != null && (codename.equals("golden") + || codename.equals("goldenlteatt") + || codename.equals("goldenltebmc") + || codename.equals("goldenltevzw") + || codename.equals("goldenve3g"))) + || (model != null && (model.equals("GT-I8190") + || model.equals("GT-I8190L") + || model.equals("GT-I8190N") + || model.equals("GT-I8190T") + || model.equals("GT-I8200L") + || model.equals("SAMSUNG-SM-G730A") + || model.equals("SM-G730V") + || model.equals("SM-G730W8")))) { + return "Galaxy S3 Mini"; + } + if ((codename != null && (codename.equals("goldenve3g") + || codename.equals("goldenvess3g"))) + || (model != null && (model.equals("GT-I8200") + || model.equals("GT-I8200N") + || model.equals("GT-I8200Q")))) { + return "Galaxy S3 Mini Value Edition"; + } + if ((codename != null && (codename.equals("s3ve3g") + || codename.equals("s3ve3gdd") + || codename.equals("s3ve3gds") + || codename.equals("s3ve3gdsdd"))) + || (model != null && (model.equals("GT-I9300I") + || model.equals("GT-I9301I") + || model.equals("GT-I9301Q")))) { + return "Galaxy S3 Neo"; + } + if ((codename != null && (codename.equals("SC-04E") + || codename.equals("ja3g") + || codename.equals("ja3gduosctc") + || codename.equals("jaltektt") + || codename.equals("jaltelgt") + || codename.equals("jalteskt") + || codename.equals("jflte") + || codename.equals("jflteMetroPCS") + || codename.equals("jflteaio") + || codename.equals("jflteatt") + || codename.equals("jfltecan") + || codename.equals("jfltecri") + || codename.equals("jfltecsp") + || codename.equals("jfltelra") + || codename.equals("jflterefreshspr") + || codename.equals("jfltespr") + || codename.equals("jfltetfnatt") + || codename.equals("jfltetfntmo") + || codename.equals("jfltetmo") + || codename.equals("jflteusc") + || codename.equals("jfltevzw") + || codename.equals("jfltevzwpp") + || codename.equals("jftdd") + || codename.equals("jfvelte") + || codename.equals("jfwifi") + || codename.equals("jsglte") + || codename.equals("ks01lte") + || codename.equals("ks01ltektt") + || codename.equals("ks01ltelgt"))) + || (model != null && (model.equals("GT-I9500") + || model.equals("GT-I9505") + || model.equals("GT-I9505X") + || model.equals("GT-I9506") + || model.equals("GT-I9507") + || model.equals("GT-I9507V") + || model.equals("GT-I9508") + || model.equals("GT-I9508C") + || model.equals("GT-I9508V") + || model.equals("GT-I9515") + || model.equals("GT-I9515L") + || model.equals("SAMSUNG-SGH-I337") + || model.equals("SAMSUNG-SGH-I337Z") + || model.equals("SC-04E") + || model.equals("SCH-I545") + || model.equals("SCH-I545L") + || model.equals("SCH-I545PP") + || model.equals("SCH-I959") + || model.equals("SCH-R970") + || model.equals("SCH-R970C") + || model.equals("SCH-R970X") + || model.equals("SGH-I337M") + || model.equals("SGH-M919") + || model.equals("SGH-M919N") + || model.equals("SGH-M919V") + || model.equals("SGH-S970G") + || model.equals("SHV-E300K") + || model.equals("SHV-E300L") + || model.equals("SHV-E300S") + || model.equals("SHV-E330K") + || model.equals("SHV-E330L") + || model.equals("SM-S975L") + || model.equals("SPH-L720") + || model.equals("SPH-L720T")))) { + return "Galaxy S4"; + } + if ((codename != null && (codename.equals("serrano3g") + || codename.equals("serranods") + || codename.equals("serranolte") + || codename.equals("serranoltebmc") + || codename.equals("serranoltektt") + || codename.equals("serranoltekx") + || codename.equals("serranoltelra") + || codename.equals("serranoltespr") + || codename.equals("serranolteusc") + || codename.equals("serranoltevzw") + || codename.equals("serranove3g") + || codename.equals("serranovelte") + || codename.equals("serranovolteatt"))) + || (model != null && (model.equals("GT-I9190") + || model.equals("GT-I9192") + || model.equals("GT-I9192I") + || model.equals("GT-I9195") + || model.equals("GT-I9195I") + || model.equals("GT-I9195L") + || model.equals("GT-I9195T") + || model.equals("GT-I9195X") + || model.equals("GT-I9197") + || model.equals("SAMSUNG-SGH-I257") + || model.equals("SCH-I435") + || model.equals("SCH-I435L") + || model.equals("SCH-R890") + || model.equals("SGH-I257M") + || model.equals("SHV-E370D") + || model.equals("SHV-E370K") + || model.equals("SPH-L520")))) { + return "Galaxy S4 Mini"; + } + if ((codename != null && (codename.equals("SC-04F") + || codename.equals("SCL23") + || codename.equals("k3g") + || codename.equals("klte") + || codename.equals("klteMetroPCS") + || codename.equals("klteacg") + || codename.equals("klteaio") + || codename.equals("klteatt") + || codename.equals("kltecan") + || codename.equals("klteduoszn") + || codename.equals("kltektt") + || codename.equals("kltelgt") + || codename.equals("kltelra") + || codename.equals("klteskt") + || codename.equals("kltespr") + || codename.equals("kltetfnvzw") + || codename.equals("kltetmo") + || codename.equals("klteusc") + || codename.equals("kltevzw") + || codename.equals("kwifi") + || codename.equals("lentisltektt") + || codename.equals("lentisltelgt") + || codename.equals("lentislteskt"))) + || (model != null && (model.equals("SAMSUNG-SM-G900A") + || model.equals("SAMSUNG-SM-G900AZ") + || model.equals("SC-04F") + || model.equals("SCL23") + || model.equals("SM-G9006W") + || model.equals("SM-G9008W") + || model.equals("SM-G9009W") + || model.equals("SM-G900F") + || model.equals("SM-G900FQ") + || model.equals("SM-G900H") + || model.equals("SM-G900I") + || model.equals("SM-G900K") + || model.equals("SM-G900L") + || model.equals("SM-G900M") + || model.equals("SM-G900MD") + || model.equals("SM-G900P") + || model.equals("SM-G900R4") + || model.equals("SM-G900R6") + || model.equals("SM-G900R7") + || model.equals("SM-G900S") + || model.equals("SM-G900T") + || model.equals("SM-G900T1") + || model.equals("SM-G900T3") + || model.equals("SM-G900T4") + || model.equals("SM-G900V") + || model.equals("SM-G900W8") + || model.equals("SM-G900X") + || model.equals("SM-G906K") + || model.equals("SM-G906L") + || model.equals("SM-G906S") + || model.equals("SM-S903VL")))) { + return "Galaxy S5"; + } + if ((codename != null && (codename.equals("s5neolte") + || codename.equals("s5neoltecan"))) + || (model != null && (model.equals("SM-G903F") + || model.equals("SM-G903M") + || model.equals("SM-G903W")))) { + return "Galaxy S5 Neo"; + } + if ((codename != null && (codename.equals("SC-05G") + || codename.equals("zeroflte") + || codename.equals("zeroflteacg") + || codename.equals("zeroflteaio") + || codename.equals("zeroflteatt") + || codename.equals("zerofltebmc") + || codename.equals("zerofltechn") + || codename.equals("zerofltectc") + || codename.equals("zerofltektt") + || codename.equals("zerofltelgt") + || codename.equals("zerofltelra") + || codename.equals("zerofltemtr") + || codename.equals("zeroflteskt") + || codename.equals("zerofltespr") + || codename.equals("zerofltetfnvzw") + || codename.equals("zerofltetmo") + || codename.equals("zeroflteusc") + || codename.equals("zerofltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-G920A") + || model.equals("SAMSUNG-SM-G920AZ") + || model.equals("SC-05G") + || model.equals("SM-G9200") + || model.equals("SM-G9208") + || model.equals("SM-G9209") + || model.equals("SM-G920F") + || model.equals("SM-G920I") + || model.equals("SM-G920K") + || model.equals("SM-G920L") + || model.equals("SM-G920P") + || model.equals("SM-G920R4") + || model.equals("SM-G920R6") + || model.equals("SM-G920R7") + || model.equals("SM-G920S") + || model.equals("SM-G920T") + || model.equals("SM-G920T1") + || model.equals("SM-G920V") + || model.equals("SM-G920W8") + || model.equals("SM-G920X") + || model.equals("SM-S906L") + || model.equals("SM-S907VL")))) { + return "Galaxy S6"; + } + if ((codename != null && (codename.equals("404SC") + || codename.equals("SC-04G") + || codename.equals("SCV31") + || codename.equals("zerolte") + || codename.equals("zerolteacg") + || codename.equals("zerolteatt") + || codename.equals("zeroltebmc") + || codename.equals("zeroltechn") + || codename.equals("zeroltektt") + || codename.equals("zeroltelgt") + || codename.equals("zeroltelra") + || codename.equals("zerolteskt") + || codename.equals("zeroltespr") + || codename.equals("zeroltetmo") + || codename.equals("zerolteusc") + || codename.equals("zeroltevzw"))) + || (model != null && (model.equals("404SC") + || model.equals("SAMSUNG-SM-G925A") + || model.equals("SC-04G") + || model.equals("SCV31") + || model.equals("SM-G9250") + || model.equals("SM-G925F") + || model.equals("SM-G925I") + || model.equals("SM-G925K") + || model.equals("SM-G925L") + || model.equals("SM-G925P") + || model.equals("SM-G925R4") + || model.equals("SM-G925R6") + || model.equals("SM-G925R7") + || model.equals("SM-G925S") + || model.equals("SM-G925T") + || model.equals("SM-G925V") + || model.equals("SM-G925W8") + || model.equals("SM-G925X")))) { + return "Galaxy S6 Edge"; + } + if ((codename != null && (codename.equals("zenlte") + || codename.equals("zenlteatt") + || codename.equals("zenltebmc") + || codename.equals("zenltechn") + || codename.equals("zenltektt") + || codename.equals("zenltekx") + || codename.equals("zenltelgt") + || codename.equals("zenlteskt") + || codename.equals("zenltespr") + || codename.equals("zenltetmo") + || codename.equals("zenlteusc") + || codename.equals("zenltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-G928A") + || model.equals("SM-G9280") + || model.equals("SM-G9287") + || model.equals("SM-G9287C") + || model.equals("SM-G928C") + || model.equals("SM-G928F") + || model.equals("SM-G928G") + || model.equals("SM-G928I") + || model.equals("SM-G928K") + || model.equals("SM-G928L") + || model.equals("SM-G928N0") + || model.equals("SM-G928P") + || model.equals("SM-G928R4") + || model.equals("SM-G928S") + || model.equals("SM-G928T") + || model.equals("SM-G928V") + || model.equals("SM-G928W8") + || model.equals("SM-G928X")))) { + return "Galaxy S6 Edge+"; + } + if ((codename != null && (codename.equals("herolte") + || codename.equals("heroltebmc") + || codename.equals("heroltektt") + || codename.equals("heroltelgt") + || codename.equals("herolteskt") + || codename.equals("heroqlteacg") + || codename.equals("heroqlteaio") + || codename.equals("heroqlteatt") + || codename.equals("heroqltecctvzw") + || codename.equals("heroqltechn") + || codename.equals("heroqltelra") + || codename.equals("heroqltemtr") + || codename.equals("heroqltespr") + || codename.equals("heroqltetfnvzw") + || codename.equals("heroqltetmo") + || codename.equals("heroqlteue") + || codename.equals("heroqlteusc") + || codename.equals("heroqltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-G930A") + || model.equals("SAMSUNG-SM-G930AZ") + || model.equals("SM-G9300") + || model.equals("SM-G9308") + || model.equals("SM-G930F") + || model.equals("SM-G930K") + || model.equals("SM-G930L") + || model.equals("SM-G930P") + || model.equals("SM-G930R4") + || model.equals("SM-G930R6") + || model.equals("SM-G930R7") + || model.equals("SM-G930S") + || model.equals("SM-G930T") + || model.equals("SM-G930T1") + || model.equals("SM-G930U") + || model.equals("SM-G930V") + || model.equals("SM-G930VC") + || model.equals("SM-G930VL") + || model.equals("SM-G930W8") + || model.equals("SM-G930X")))) { + return "Galaxy S7"; + } + if ((codename != null && (codename.equals("SC-02H") + || codename.equals("SCV33") + || codename.equals("hero2lte") + || codename.equals("hero2ltebmc") + || codename.equals("hero2ltektt") + || codename.equals("hero2ltelgt") + || codename.equals("hero2lteskt") + || codename.equals("hero2qlteatt") + || codename.equals("hero2qltecctvzw") + || codename.equals("hero2qltechn") + || codename.equals("hero2qltespr") + || codename.equals("hero2qltetmo") + || codename.equals("hero2qlteue") + || codename.equals("hero2qlteusc") + || codename.equals("hero2qltevzw"))) + || (model != null && (model.equals("SAMSUNG-SM-G935A") + || model.equals("SC-02H") + || model.equals("SCV33") + || model.equals("SM-G9350") + || model.equals("SM-G935F") + || model.equals("SM-G935K") + || model.equals("SM-G935L") + || model.equals("SM-G935P") + || model.equals("SM-G935R4") + || model.equals("SM-G935S") + || model.equals("SM-G935T") + || model.equals("SM-G935U") + || model.equals("SM-G935V") + || model.equals("SM-G935VC") + || model.equals("SM-G935W8") + || model.equals("SM-G935X")))) { + return "Galaxy S7 Edge"; + } + if ((codename != null && (codename.equals("SC-02J") + || codename.equals("SCV36") + || codename.equals("dreamlte") + || codename.equals("dreamlteks") + || codename.equals("dreamqltecan") + || codename.equals("dreamqltechn") + || codename.equals("dreamqltecmcc") + || codename.equals("dreamqltesq") + || codename.equals("dreamqlteue"))) + || (model != null && (model.equals("SC-02J") + || model.equals("SCV36") + || model.equals("SM-G9500") + || model.equals("SM-G9508") + || model.equals("SM-G950F") + || model.equals("SM-G950N") + || model.equals("SM-G950U") + || model.equals("SM-G950U1") + || model.equals("SM-G950W")))) { + return "Galaxy S8"; + } + if ((codename != null && (codename.equals("SC-03J") + || codename.equals("SCV35") + || codename.equals("dream2lte") + || codename.equals("dream2lteks") + || codename.equals("dream2qltecan") + || codename.equals("dream2qltechn") + || codename.equals("dream2qltesq") + || codename.equals("dream2qlteue"))) + || (model != null && (model.equals("SC-03J") + || model.equals("SCV35") + || model.equals("SM-G9550") + || model.equals("SM-G955F") + || model.equals("SM-G955N") + || model.equals("SM-G955U") + || model.equals("SM-G955U1") + || model.equals("SM-G955W")))) { + return "Galaxy S8+"; + } + if ((codename != null && (codename.equals("starlte") + || codename.equals("starlteks") + || codename.equals("starqltechn") + || codename.equals("starqltecmcc") + || codename.equals("starqltecs") + || codename.equals("starqltesq") + || codename.equals("starqlteue"))) + || (model != null && (model.equals("SM-G9600") + || model.equals("SM-G9608") + || model.equals("SM-G960F") + || model.equals("SM-G960N") + || model.equals("SM-G960U") + || model.equals("SM-G960U1") + || model.equals("SM-G960W")))) { + return "Galaxy S9"; + } + if ((codename != null && (codename.equals("star2lte") + || codename.equals("star2lteks") + || codename.equals("star2qltechn") + || codename.equals("star2qltecs") + || codename.equals("star2qltesq") + || codename.equals("star2qlteue"))) + || (model != null && (model.equals("SM-G9650") + || model.equals("SM-G965F") + || model.equals("SM-G965N") + || model.equals("SM-G965U") + || model.equals("SM-G965U1") + || model.equals("SM-G965W")))) { + return "Galaxy S9+"; + } + if ((codename != null && (codename.equals("GT-P7500") + || codename.equals("GT-P7500D") + || codename.equals("GT-P7503") + || codename.equals("GT-P7510") + || codename.equals("SC-01D") + || codename.equals("SCH-I905") + || codename.equals("SGH-T859") + || codename.equals("SHW-M300W") + || codename.equals("SHW-M380K") + || codename.equals("SHW-M380S") + || codename.equals("SHW-M380W"))) + || (model != null && (model.equals("GT-P7500") + || model.equals("GT-P7500D") + || model.equals("GT-P7503") + || model.equals("GT-P7510") + || model.equals("SC-01D") + || model.equals("SCH-I905") + || model.equals("SGH-T859") + || model.equals("SHW-M300W") + || model.equals("SHW-M380K") + || model.equals("SHW-M380S") + || model.equals("SHW-M380W")))) { + return "Galaxy Tab 10.1"; + } + if ((codename != null && (codename.equals("GT-P6200") + || codename.equals("GT-P6200L") + || codename.equals("GT-P6201") + || codename.equals("GT-P6210") + || codename.equals("GT-P6211") + || codename.equals("SC-02D") + || codename.equals("SGH-T869") + || codename.equals("SHW-M430W"))) + || (model != null && (model.equals("GT-P6200") + || model.equals("GT-P6200L") + || model.equals("GT-P6201") + || model.equals("GT-P6210") + || model.equals("GT-P6211") + || model.equals("SC-02D") + || model.equals("SGH-T869") + || model.equals("SHW-M430W")))) { + return "Galaxy Tab 7.0 Plus"; + } + if ((codename != null && (codename.equals("gteslteatt") + || codename.equals("gtesltebmc") + || codename.equals("gtesltelgt") + || codename.equals("gteslteskt") + || codename.equals("gtesltetmo") + || codename.equals("gtesltetw") + || codename.equals("gtesltevzw") + || codename.equals("gtesqltespr") + || codename.equals("gtesqlteusc"))) + || (model != null && (model.equals("SAMSUNG-SM-T377A") + || model.equals("SM-T375L") + || model.equals("SM-T375S") + || model.equals("SM-T3777") + || model.equals("SM-T377P") + || model.equals("SM-T377R4") + || model.equals("SM-T377T") + || model.equals("SM-T377V") + || model.equals("SM-T377W")))) { + return "Galaxy Tab E 8.0"; + } + if ((codename != null && (codename.equals("gtel3g") + || codename.equals("gtelltevzw") + || codename.equals("gtelwifi") + || codename.equals("gtelwifichn") + || codename.equals("gtelwifiue"))) + || (model != null && (model.equals("SM-T560") + || model.equals("SM-T560NU") + || model.equals("SM-T561") + || model.equals("SM-T561M") + || model.equals("SM-T561Y") + || model.equals("SM-T562") + || model.equals("SM-T567V")))) { + return "Galaxy Tab E 9.6"; + } + if ((codename != null && (codename.equals("403SC") + || codename.equals("degas2wifi") + || codename.equals("degas2wifibmwchn") + || codename.equals("degas3g") + || codename.equals("degaslte") + || codename.equals("degasltespr") + || codename.equals("degasltevzw") + || codename.equals("degasvelte") + || codename.equals("degasveltechn") + || codename.equals("degaswifi") + || codename.equals("degaswifibmwzc") + || codename.equals("degaswifidtv") + || codename.equals("degaswifiopenbnn") + || codename.equals("degaswifiue"))) + || (model != null && (model.equals("403SC") + || model.equals("SM-T230") + || model.equals("SM-T230NT") + || model.equals("SM-T230NU") + || model.equals("SM-T230NW") + || model.equals("SM-T230NY") + || model.equals("SM-T230X") + || model.equals("SM-T231") + || model.equals("SM-T232") + || model.equals("SM-T235") + || model.equals("SM-T235Y") + || model.equals("SM-T237P") + || model.equals("SM-T237V") + || model.equals("SM-T239") + || model.equals("SM-T2397") + || model.equals("SM-T239C") + || model.equals("SM-T239M")))) { + return "Galaxy Tab4 7.0"; + } + if ((codename != null && (codename.equals("gvlte") + || codename.equals("gvlteatt") + || codename.equals("gvltevzw") + || codename.equals("gvltexsp") + || codename.equals("gvwifijpn") + || codename.equals("gvwifiue"))) + || (model != null && (model.equals("SAMSUNG-SM-T677A") + || model.equals("SM-T670") + || model.equals("SM-T677") + || model.equals("SM-T677V")))) { + return "Galaxy View"; + } + if ((codename != null && codename.equals("manta"))) { + return "Nexus 10"; + } + // ---------------------------------------------------------------------------- + // Sony + if ((codename != null && (codename.equals("D2104") + || codename.equals("D2105"))) + || (model != null && (model.equals("D2104") + || model.equals("D2105")))) { + return "Xperia E1 dual"; + } + if ((codename != null && (codename.equals("D2202") + || codename.equals("D2203") + || codename.equals("D2206") + || codename.equals("D2243"))) + || (model != null && (model.equals("D2202") + || model.equals("D2203") + || model.equals("D2206") + || model.equals("D2243")))) { + return "Xperia E3"; + } + if ((codename != null && (codename.equals("E5603") + || codename.equals("E5606") + || codename.equals("E5653"))) + || (model != null && (model.equals("E5603") + || model.equals("E5606") + || model.equals("E5653")))) { + return "Xperia M5"; + } + if ((codename != null && (codename.equals("E5633") + || codename.equals("E5643") + || codename.equals("E5663"))) + || (model != null && (model.equals("E5633") + || model.equals("E5643") + || model.equals("E5663")))) { + return "Xperia M5 Dual"; + } + if ((codename != null && codename.equals("LT26i")) + || (model != null && model.equals("LT26i"))) { + return "Xperia S"; + } + if ((codename != null && (codename.equals("D5303") + || codename.equals("D5306") + || codename.equals("D5316") + || codename.equals("D5316N") + || codename.equals("D5322"))) + || (model != null && (model.equals("D5303") + || model.equals("D5306") + || model.equals("D5316") + || model.equals("D5316N") + || model.equals("D5322")))) { + return "Xperia T2 Ultra"; + } + if ((codename != null && (codename.equals("txs03"))) + || (model != null && (model.equals("SGPT12") + || model.equals("SGPT13")))) { + return "Xperia Tablet S"; + } + if ((codename != null && (codename.equals("SGP311") + || codename.equals("SGP312") + || codename.equals("SGP321") + || codename.equals("SGP351"))) + || (model != null && (model.equals("SGP311") + || model.equals("SGP312") + || model.equals("SGP321") + || model.equals("SGP351")))) { + return "Xperia Tablet Z"; + } + if ((codename != null && (codename.equals("D6502") + || codename.equals("D6503") + || codename.equals("D6543") + || codename.equals("SO-03F"))) + || (model != null && (model.equals("D6502") + || model.equals("D6503") + || model.equals("D6543") + || model.equals("SO-03F")))) { + return "Xperia Z2"; + } + if ((codename != null && (codename.equals("401SO") + || codename.equals("D6603") + || codename.equals("D6616") + || codename.equals("D6643") + || codename.equals("D6646") + || codename.equals("D6653") + || codename.equals("SO-01G") + || codename.equals("SOL26") + || codename.equals("leo"))) + || (model != null && (model.equals("401SO") + || model.equals("D6603") + || model.equals("D6616") + || model.equals("D6643") + || model.equals("D6646") + || model.equals("D6653") + || model.equals("SO-01G") + || model.equals("SOL26")))) { + return "Xperia Z3"; + } + if ((codename != null && (codename.equals("402SO") + || codename.equals("SO-03G") + || codename.equals("SOV31"))) + || (model != null && (model.equals("402SO") + || model.equals("SO-03G") + || model.equals("SOV31")))) { + return "Xperia Z4"; + } + if ((codename != null && (codename.equals("E5803") + || codename.equals("E5823") + || codename.equals("SO-02H"))) + || (model != null && (model.equals("E5803") + || model.equals("E5823") + || model.equals("SO-02H")))) { + return "Xperia Z5 Compact"; + } + // ---------------------------------------------------------------------------- + // Sony Ericsson + if ((codename != null && (codename.equals("LT26i") + || codename.equals("SO-02D"))) + || (model != null && (model.equals("LT26i") + || model.equals("SO-02D")))) { + return "Xperia S"; + } + if ((codename != null && (codename.equals("SGP311") + || codename.equals("SGP321") + || codename.equals("SGP341") + || codename.equals("SO-03E"))) + || (model != null && (model.equals("SGP311") + || model.equals("SGP321") + || model.equals("SGP341") + || model.equals("SO-03E")))) { + return "Xperia Tablet Z"; + } + return fallback; + } + + /** + * Get the {@link DeviceInfo} for the current device. Do not run on the UI thread, as this may + * download JSON to retrieve the {@link DeviceInfo}. JSON is only downloaded once and then + * stored to {@link SharedPreferences}. + * + * @param context + * the application context. + * @return {@link DeviceInfo} for the current device. + */ + @WorkerThread + public static DeviceInfo getDeviceInfo(Context context) { + return getDeviceInfo(context.getApplicationContext(), Build.DEVICE, Build.MODEL); + } + + /** + * Get the {@link DeviceInfo} for the current device. Do not run on the UI thread, as this may + * download JSON to retrieve the {@link DeviceInfo}. JSON is only downloaded once and then + * stored to {@link SharedPreferences}. + * + * @param context + * the application context. + * @param codename + * the codename of the device + * @return {@link DeviceInfo} for the current device. + */ + @WorkerThread + public static DeviceInfo getDeviceInfo(Context context, String codename) { + return getDeviceInfo(context, codename, null); + } + + /** + * Get the {@link DeviceInfo} for the current device. Do not run on the UI thread, as this may + * download JSON to retrieve the {@link DeviceInfo}. JSON is only downloaded once and then + * stored to {@link SharedPreferences}. + * + * @param context + * the application context. + * @param codename + * the codename of the device + * @param model + * the model of the device + * @return {@link DeviceInfo} for the current device. + */ + @WorkerThread + public static DeviceInfo getDeviceInfo(Context context, String codename, String model) { + SharedPreferences prefs = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE); + String key = String.format("%s:%s", codename, model); + String savedJson = prefs.getString(key, null); + if (savedJson != null) { + try { + return new DeviceInfo(new JSONObject(savedJson)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + // check if we have an internet connection + int ret = context.checkCallingOrSelfPermission(Manifest.permission.ACCESS_NETWORK_STATE); + boolean isConnectedToNetwork = false; + if (ret == PackageManager.PERMISSION_GRANTED) { + ConnectivityManager connMgr = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); + if (networkInfo != null && networkInfo.isConnected()) { + isConnectedToNetwork = true; + } + } else { + // assume we are connected. + isConnectedToNetwork = true; + } + + if (isConnectedToNetwork) { + try { + // Get the device name from the generated JSON files created from Google's device list. + String url = String.format(DEVICE_JSON_URL, codename.toLowerCase(Locale.ENGLISH)); + String jsonString = downloadJson(url); + JSONArray jsonArray = new JSONArray(jsonString); + for (int i = 0, len = jsonArray.length(); i < len; i++) { + JSONObject json = jsonArray.getJSONObject(i); + DeviceInfo info = new DeviceInfo(json); + if ((codename.equalsIgnoreCase(info.codename) && model == null) + || codename.equalsIgnoreCase(info.codename) && model.equalsIgnoreCase(info.model)) { + // Save to SharedPreferences so we don't need to make another request. + SharedPreferences.Editor editor = prefs.edit(); + editor.putString(key, json.toString()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + editor.apply(); + } else { + editor.commit(); + } + return info; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (codename.equals(Build.DEVICE) && model.equals(Build.MODEL)) { + return new DeviceInfo(Build.MANUFACTURER, getDeviceName(), codename, model); // current device + } + + return new DeviceInfo(null, null, codename, model); // unknown device + } + + /** + *

Capitalizes getAllProcesses the whitespace separated words in a String. Only the first + * letter of each word is changed.

+ * + * Whitespace is defined by {@link Character#isWhitespace(char)}. + * + * @param str + * the String to capitalize + * @return capitalized The capitalized String + */ + private static String capitalize(String str) { + if (TextUtils.isEmpty(str)) { + return str; + } + char[] arr = str.toCharArray(); + boolean capitalizeNext = true; + String phrase = ""; + for (char c : arr) { + if (capitalizeNext && Character.isLetter(c)) { + phrase += Character.toUpperCase(c); + capitalizeNext = false; + continue; + } else if (Character.isWhitespace(c)) { + capitalizeNext = true; + } + phrase += c; + } + return phrase; + } + + /** Download URL to String */ + @WorkerThread + private static String downloadJson(String myurl) throws IOException { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try { + URL url = new URL(myurl); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setReadTimeout(10000); + conn.setConnectTimeout(15000); + conn.setRequestMethod("GET"); + conn.setDoInput(true); + conn.connect(); + if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { + reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append('\n'); + } + } + return sb.toString(); + } finally { + if (reader != null) { + reader.close(); + } + } + } + + public static final class Request { + + final Context context; + final Handler handler; + String codename; + String model; + + private Request(Context ctx) { + context = ctx; + handler = new Handler(ctx.getMainLooper()); + } + + /** + * Set the device codename to query. You should also set the model. + * + * @param codename + * the value of the system property "ro.product.device" + * @return This Request object to allow for chaining of calls to set methods. + * @see Build#DEVICE + */ + public Request setCodename(String codename) { + this.codename = codename; + return this; + } + + /** + * Set the device model to query. You should also set the codename. + * + * @param model + * the value of the system property "ro.product.model" + * @return This Request object to allow for chaining of calls to set methods. + * @see Build#MODEL + */ + public Request setModel(String model) { + this.model = model; + return this; + } + + /** + * Download information about the device. This saves the results in shared-preferences so + * future requests will not need a network connection. + * + * @param callback + * the callback to retrieve the {@link DeviceInfo} + */ + public void request(Callback callback) { + if (codename == null && model == null) { + codename = Build.DEVICE; + model = Build.MODEL; + } + GetDeviceRunnable runnable = new GetDeviceRunnable(callback); + if (Looper.myLooper() == Looper.getMainLooper()) { + new Thread(runnable).start(); + } else { + runnable.run(); // already running in background thread. + } + } + + private final class GetDeviceRunnable implements Runnable { + + final Callback callback; + DeviceInfo deviceInfo; + Exception error; + + public GetDeviceRunnable(Callback callback) { + this.callback = callback; + } + + @Override public void run() { + try { + deviceInfo = getDeviceInfo(context, codename, model); + } catch (Exception e) { + error = e; + } + handler.post(new Runnable() { + + @Override public void run() { + callback.onFinished(deviceInfo, error); + } + }); + } + } + + } + + /** + * Callback which is invoked when the {@link DeviceInfo} is finished loading. + */ + public interface Callback { + + /** + * Callback to get the device info. This is run on the UI thread. + * + * @param info + * the requested {@link DeviceInfo} + * @param error + * {@code null} if nothing went wrong. + */ + void onFinished(DeviceInfo info, Exception error); + } + + /** + * Device information based on + * Google's maintained list. + */ + public static final class DeviceInfo { + + /** Retail branding */ + public final String manufacturer; + + /** Marketing name */ + public final String marketName; + + /** the value of the system property "ro.product.device" */ + public final String codename; + + /** the value of the system property "ro.product.model" */ + public final String model; + + public DeviceInfo(String manufacturer, String marketName, String codename, String model) { + this.manufacturer = manufacturer; + this.marketName = marketName; + this.codename = codename; + this.model = model; + } + + private DeviceInfo(JSONObject jsonObject) throws JSONException { + manufacturer = jsonObject.getString("manufacturer"); + marketName = jsonObject.getString("market_name"); + codename = jsonObject.getString("codename"); + model = jsonObject.getString("model"); + } + + /** + * @return the consumer friendly name of the device. + */ + public String getName() { + if (!TextUtils.isEmpty(marketName)) { + return marketName; + } + return capitalize(model); + } + } + +} + diff --git a/app/src/main/java/com/tangem/wallet/Digest.java b/app/src/main/java/com/tangem/wallet/Digest.java index b3924d61c7..51e1e70c58 100644 --- a/app/src/main/java/com/tangem/wallet/Digest.java +++ b/app/src/main/java/com/tangem/wallet/Digest.java @@ -1,114 +1,114 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 18.12.2017. - */ - -public interface Digest{ - - /** - * Insert one more input data byte. - * - * @param in the input byte - */ - void update(byte in); - - /** - * Insert some more bytes. - * - * @param inbuf the data bytes - */ - void update(byte[] inbuf); - - /** - * Insert some more bytes. - * - * @param inbuf the data buffer - * @param off the data offset in {@code inbuf} - * @param len the data length (in bytes) - */ - void update(byte[] inbuf, int off, int len); - - /** - * Finalize the current hash computation and return the hash value - * in a newly-allocated array. The object is resetted. - * - * @return the hash output - */ - byte[] digest(); - - /** - * Input some bytes, then finalize the current hash computation - * and return the hash value in a newly-allocated array. The object - * is resetted. - * - * @param inbuf the input data - * @return the hash output - */ - byte[] digest(byte[] inbuf); - - /** - * Finalize the current hash computation and store the hash value - * in the provided output buffer. The {@code len} parameter - * contains the maximum number of bytes that should be written; - * no more bytes than the natural hash function output length will - * be produced. If {@code len} is smaller than the natural - * hash output length, the hash output is truncated to its first - * {@code len} bytes. The object is resetted. - * - * @param outbuf the output buffer - * @param off the output offset within {@code outbuf} - * @param len the requested hash output length (in bytes) - * @return the number of bytes actually written in {@code outbuf} - */ - int digest(byte[] outbuf, int off, int len); - - /** - * Get the natural hash function output length (in bytes). - * - * @return the digest output length (in bytes) - */ - int getDigestLength(); - - /** - * Reset the object: this makes it suitable for a new hash - * computation. The current computation, if any, is discarded. - */ - void reset(); - - /** - * Clone the current state. The returned object evolves independantly - * of this object. - * - * @return the clone - */ - Digest copy(); - - /** - *

Return the "block length" for the hash function. This - * value is naturally defined for iterated hash functions - * (Merkle-Damgard). It is used in HMAC (that's what the - * HMAC specification - * names the "{@code B}" parameter).

- * - *

If the function is "block-less" then this function may - * return {@code -n} where {@code n} is an integer such that the - * block length for HMAC ("{@code B}") will be inferred from the - * key length, by selecting the smallest multiple of {@code n} - * which is no smaller than the key length. For instance, for - * the Fugue-xxx hash functions, this function returns -4: the - * virtual block length B is the HMAC key length, rounded up to - * the next multiple of 4.

- * - * @return the internal block length (in bytes), or {@code -n} - */ - int getBlockLength(); - - /** - *

Get the display name for this function (e.g. {@code "SHA-1"} - * for SHA-1).

- * - * @see Object - */ - String toString(); +package com.tangem.wallet; + +/** + * Created by Ilia on 18.12.2017. + */ + +public interface Digest{ + + /** + * Insert one more input data byte. + * + * @param in the input byte + */ + void update(byte in); + + /** + * Insert some more bytes. + * + * @param inbuf the data bytes + */ + void update(byte[] inbuf); + + /** + * Insert some more bytes. + * + * @param inbuf the data buffer + * @param off the data offset in {@code inbuf} + * @param len the data length (in bytes) + */ + void update(byte[] inbuf, int off, int len); + + /** + * Finalize the current hash computation and return the hash value + * in a newly-allocated array. The object is resetted. + * + * @return the hash output + */ + byte[] digest(); + + /** + * Input some bytes, then finalize the current hash computation + * and return the hash value in a newly-allocated array. The object + * is resetted. + * + * @param inbuf the input data + * @return the hash output + */ + byte[] digest(byte[] inbuf); + + /** + * Finalize the current hash computation and store the hash value + * in the provided output buffer. The {@code len} parameter + * contains the maximum number of bytes that should be written; + * no more bytes than the natural hash function output length will + * be produced. If {@code len} is smaller than the natural + * hash output length, the hash output is truncated to its first + * {@code len} bytes. The object is resetted. + * + * @param outbuf the output buffer + * @param off the output offset within {@code outbuf} + * @param len the requested hash output length (in bytes) + * @return the number of bytes actually written in {@code outbuf} + */ + int digest(byte[] outbuf, int off, int len); + + /** + * Get the natural hash function output length (in bytes). + * + * @return the digest output length (in bytes) + */ + int getDigestLength(); + + /** + * Reset the object: this makes it suitable for a new hash + * computation. The current computation, if any, is discarded. + */ + void reset(); + + /** + * Clone the current state. The returned object evolves independantly + * of this object. + * + * @return the clone + */ + Digest copy(); + + /** + *

Return the "block length" for the hash function. This + * value is naturally defined for iterated hash functions + * (Merkle-Damgard). It is used in HMAC (that's what the + * HMAC specification + * names the "{@code B}" parameter).

+ * + *

If the function is "block-less" then this function may + * return {@code -n} where {@code n} is an integer such that the + * block length for HMAC ("{@code B}") will be inferred from the + * key length, by selecting the smallest multiple of {@code n} + * which is no smaller than the key length. For instance, for + * the Fugue-xxx hash functions, this function returns -4: the + * virtual block length B is the HMAC key length, rounded up to + * the next multiple of 4.

+ * + * @return the internal block length (in bytes), or {@code -n} + */ + int getBlockLength(); + + /** + *

Get the display name for this function (e.g. {@code "SHA-1"} + * for SHA-1).

+ * + * @see Object + */ + String toString(); } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/DigestEngine.java b/app/src/main/java/com/tangem/wallet/DigestEngine.java index 8dd130353d..3eba8e60c8 100644 --- a/app/src/main/java/com/tangem/wallet/DigestEngine.java +++ b/app/src/main/java/com/tangem/wallet/DigestEngine.java @@ -1,220 +1,220 @@ -package com.tangem.wallet; - - -import java.security.MessageDigest; - -/** - * Created by Ilia on 18.12.2017. - */ - -public abstract class DigestEngine extends MessageDigest implements Digest { - - /** - * Reset the hash algorithm state. - */ - protected abstract void engineReset(); - - /** - * Process one block of data. - * - * @param data the data block - */ - protected abstract void processBlock(byte[] data); - - /** - * Perform the final padding and store the result in the - * provided buffer. This method shall call {@link #flush} - * and then {@link #update} with the appropriate padding - * data in order to get the full input data. - * - * @param buf the output buffer - * @param off the output offset - */ - protected abstract void doPadding(byte[] buf, int off); - - /** - * This function is called at object creation time; the - * implementation should use it to perform initialization tasks. - * After this method is called, the implementation should be ready - * to process data or meaningfully honour calls such as - * {@link #engineGetDigestLength} - */ - protected abstract void doInit(); - - private int digestLen, blockLen, inputLen; - private byte[] inputBuf, outputBuf; - private long blockCount; - - /** - * Instantiate the engine. - */ - public DigestEngine(String alg) - { - super(alg); - doInit(); - digestLen = engineGetDigestLength(); - blockLen = getInternalBlockLength(); - inputBuf = new byte[blockLen]; - outputBuf = new byte[digestLen]; - inputLen = 0; - blockCount = 0; - } - - private void adjustDigestLen() - { - if (digestLen == 0) { - digestLen = engineGetDigestLength(); - outputBuf = new byte[digestLen]; - } - } - - public byte[] digest() - { - adjustDigestLen(); - byte[] result = new byte[digestLen]; - digest(result, 0, digestLen); - return result; - } - - public byte[] digest(byte[] input) - { - update(input, 0, input.length); - return digest(); - } - - public int digest(byte[] buf, int offset, int len) - { - adjustDigestLen(); - if (len >= digestLen) { - doPadding(buf, offset); - reset(); - return digestLen; - } else { - doPadding(outputBuf, 0); - System.arraycopy(outputBuf, 0, buf, offset, len); - reset(); - return len; - } - } - - public void reset() - { - engineReset(); - inputLen = 0; - blockCount = 0; - } - - public void update(byte input) - { - inputBuf[inputLen ++] = (byte)input; - if (inputLen == blockLen) { - processBlock(inputBuf); - blockCount ++; - inputLen = 0; - } - } - - public void update(byte[] input) - { - update(input, 0, input.length); - } - - public void update(byte[] input, int offset, int len) - { - while (len > 0) { - int copyLen = blockLen - inputLen; - if (copyLen > len) - copyLen = len; - System.arraycopy(input, offset, inputBuf, inputLen, - copyLen); - offset += copyLen; - inputLen += copyLen; - len -= copyLen; - if (inputLen == blockLen) { - processBlock(inputBuf); - blockCount ++; - inputLen = 0; - } - } - } - - /** - * Get the internal block length. This is the length (in - * bytes) of the array which will be passed as parameter to - * {@link #processBlock}. The default implementation of this - * method calls {@link #getBlockLength} and returns the same - * value. Overriding this method is useful when the advertised - * block length (which is used, for instance, by HMAC) is - * suboptimal with regards to internal buffering needs. - * - * @return the internal block length (in bytes) - */ - protected int getInternalBlockLength() - { - return getBlockLength(); - } - - /** - * Flush internal buffers, so that less than a block of data - * may at most be upheld. - * - * @return the number of bytes still unprocessed after the flush - */ - protected final int flush() - { - return inputLen; - } - - /** - * Get a reference to an internal buffer with the same size - * than a block. The contents of that buffer are defined only - * immediately after a call to {@link #flush()}: if - * {@link #flush()} return the value {@code n}, then the - * first {@code n} bytes of the array returned by this method - * are the {@code n} bytes of input data which are still - * unprocessed. The values of the remaining bytes are - * undefined and may be altered at will. - * - * @return a block-sized internal buffer - */ - protected final byte[] getBlockBuffer() - { - return inputBuf; - } - - /** - * Get the "block count": this is the number of times the - * {@link #processBlock} method has been invoked for the - * current hash operation. That counter is incremented - * after the call to {@link #processBlock}. - * - * @return the block count - */ - protected long getBlockCount() - { - return blockCount; - } - - /** - * This function copies the internal buffering state to some - * other instance of a class extending {@code DigestEngine}. - * It returns a reference to the copy. This method is intended - * to be called by the implementation of the {@link #copy} - * method. - * - * @param dest the copy - * @return the value {@code dest} - */ - protected Digest copyState(DigestEngine dest) - { - dest.inputLen = inputLen; - dest.blockCount = blockCount; - System.arraycopy(inputBuf, 0, dest.inputBuf, 0, - inputBuf.length); - adjustDigestLen(); - dest.adjustDigestLen(); - System.arraycopy(outputBuf, 0, dest.outputBuf, 0, - outputBuf.length); - return dest; - } -} +package com.tangem.wallet; + + +import java.security.MessageDigest; + +/** + * Created by Ilia on 18.12.2017. + */ + +public abstract class DigestEngine extends MessageDigest implements Digest { + + /** + * Reset the hash algorithm state. + */ + protected abstract void engineReset(); + + /** + * Process one block of data. + * + * @param data the data block + */ + protected abstract void processBlock(byte[] data); + + /** + * Perform the final padding and store the result in the + * provided buffer. This method shall call {@link #flush} + * and then {@link #update} with the appropriate padding + * data in order to get the full input data. + * + * @param buf the output buffer + * @param off the output offset + */ + protected abstract void doPadding(byte[] buf, int off); + + /** + * This function is called at object creation time; the + * implementation should use it to perform initialization tasks. + * After this method is called, the implementation should be ready + * to process data or meaningfully honour calls such as + * {@link #engineGetDigestLength} + */ + protected abstract void doInit(); + + private int digestLen, blockLen, inputLen; + private byte[] inputBuf, outputBuf; + private long blockCount; + + /** + * Instantiate the engine. + */ + public DigestEngine(String alg) + { + super(alg); + doInit(); + digestLen = engineGetDigestLength(); + blockLen = getInternalBlockLength(); + inputBuf = new byte[blockLen]; + outputBuf = new byte[digestLen]; + inputLen = 0; + blockCount = 0; + } + + private void adjustDigestLen() + { + if (digestLen == 0) { + digestLen = engineGetDigestLength(); + outputBuf = new byte[digestLen]; + } + } + + public byte[] digest() + { + adjustDigestLen(); + byte[] result = new byte[digestLen]; + digest(result, 0, digestLen); + return result; + } + + public byte[] digest(byte[] input) + { + update(input, 0, input.length); + return digest(); + } + + public int digest(byte[] buf, int offset, int len) + { + adjustDigestLen(); + if (len >= digestLen) { + doPadding(buf, offset); + reset(); + return digestLen; + } else { + doPadding(outputBuf, 0); + System.arraycopy(outputBuf, 0, buf, offset, len); + reset(); + return len; + } + } + + public void reset() + { + engineReset(); + inputLen = 0; + blockCount = 0; + } + + public void update(byte input) + { + inputBuf[inputLen ++] = (byte)input; + if (inputLen == blockLen) { + processBlock(inputBuf); + blockCount ++; + inputLen = 0; + } + } + + public void update(byte[] input) + { + update(input, 0, input.length); + } + + public void update(byte[] input, int offset, int len) + { + while (len > 0) { + int copyLen = blockLen - inputLen; + if (copyLen > len) + copyLen = len; + System.arraycopy(input, offset, inputBuf, inputLen, + copyLen); + offset += copyLen; + inputLen += copyLen; + len -= copyLen; + if (inputLen == blockLen) { + processBlock(inputBuf); + blockCount ++; + inputLen = 0; + } + } + } + + /** + * Get the internal block length. This is the length (in + * bytes) of the array which will be passed as parameter to + * {@link #processBlock}. The default implementation of this + * method calls {@link #getBlockLength} and returns the same + * value. Overriding this method is useful when the advertised + * block length (which is used, for instance, by HMAC) is + * suboptimal with regards to internal buffering needs. + * + * @return the internal block length (in bytes) + */ + protected int getInternalBlockLength() + { + return getBlockLength(); + } + + /** + * Flush internal buffers, so that less than a block of data + * may at most be upheld. + * + * @return the number of bytes still unprocessed after the flush + */ + protected final int flush() + { + return inputLen; + } + + /** + * Get a reference to an internal buffer with the same size + * than a block. The contents of that buffer are defined only + * immediately after a call to {@link #flush()}: if + * {@link #flush()} return the value {@code n}, then the + * first {@code n} bytes of the array returned by this method + * are the {@code n} bytes of input data which are still + * unprocessed. The values of the remaining bytes are + * undefined and may be altered at will. + * + * @return a block-sized internal buffer + */ + protected final byte[] getBlockBuffer() + { + return inputBuf; + } + + /** + * Get the "block count": this is the number of times the + * {@link #processBlock} method has been invoked for the + * current hash operation. That counter is incremented + * after the call to {@link #processBlock}. + * + * @return the block count + */ + protected long getBlockCount() + { + return blockCount; + } + + /** + * This function copies the internal buffering state to some + * other instance of a class extending {@code DigestEngine}. + * It returns a reference to the copy. This method is intended + * to be called by the implementation of the {@link #copy} + * method. + * + * @param dest the copy + * @return the value {@code dest} + */ + protected Digest copyState(DigestEngine dest) + { + dest.inputLen = inputLen; + dest.blockCount = blockCount; + System.arraycopy(inputBuf, 0, dest.inputBuf, 0, + inputBuf.length); + adjustDigestLen(); + dest.adjustDigestLen(); + System.arraycopy(outputBuf, 0, dest.outputBuf, 0, + outputBuf.length); + return dest; + } +} diff --git a/app/src/main/java/com/tangem/wallet/ECDSASignature_ETH.java b/app/src/main/java/com/tangem/wallet/ECDSASignature_ETH.java index 98d2f3e47f..1ad20a2d6f 100644 --- a/app/src/main/java/com/tangem/wallet/ECDSASignature_ETH.java +++ b/app/src/main/java/com/tangem/wallet/ECDSASignature_ETH.java @@ -1,52 +1,52 @@ -package com.tangem.wallet; - -import java.math.BigInteger; - -/** - * Created by Ilia on 07.01.2018. - */ - -public class ECDSASignature_ETH { - /** - * The two components of the signature. - */ - public final BigInteger r, s; - public byte v; - - /** - * Constructs a signature with the given components. Does NOT automatically canonicalise the signature. - * - * @param r - - * @param s - - */ - public ECDSASignature_ETH(BigInteger r, BigInteger s) { - this.r = r; - this.s = s; - } - - /** - *t - * @param r - * @param s - * @return - - */ - private static ECDSASignature_ETH fromComponents(byte[] r, byte[] s) { - return new ECDSASignature_ETH(new BigInteger(1, r), new BigInteger(1, s)); - } - - /** - * - * @param r - - * @param s - - * @param v - - * @return - - */ - public static ECDSASignature_ETH fromComponents(byte[] r, byte[] s, byte v) { - ECDSASignature_ETH signature = fromComponents(r, s); - signature.v = v; - return signature; - } - - -} - +package com.tangem.wallet; + +import java.math.BigInteger; + +/** + * Created by Ilia on 07.01.2018. + */ + +public class ECDSASignature_ETH { + /** + * The two components of the signature. + */ + public final BigInteger r, s; + public byte v; + + /** + * Constructs a signature with the given components. Does NOT automatically canonicalise the signature. + * + * @param r - + * @param s - + */ + public ECDSASignature_ETH(BigInteger r, BigInteger s) { + this.r = r; + this.s = s; + } + + /** + *t + * @param r + * @param s + * @return - + */ + private static ECDSASignature_ETH fromComponents(byte[] r, byte[] s) { + return new ECDSASignature_ETH(new BigInteger(1, r), new BigInteger(1, s)); + } + + /** + * + * @param r - + * @param s - + * @param v - + * @return - + */ + public static ECDSASignature_ETH fromComponents(byte[] r, byte[] s, byte v) { + ECDSASignature_ETH signature = fromComponents(r, s); + signature.v = v; + return signature; + } + + +} + diff --git a/app/src/main/java/com/tangem/wallet/ETH_Transaction.java b/app/src/main/java/com/tangem/wallet/ETH_Transaction.java index 476bc096ba..0f60b28c50 100644 --- a/app/src/main/java/com/tangem/wallet/ETH_Transaction.java +++ b/app/src/main/java/com/tangem/wallet/ETH_Transaction.java @@ -1,229 +1,229 @@ -package com.tangem.wallet; - - -import android.util.Log; - -import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Sha256Hash; -import org.spongycastle.util.BigIntegers; -import org.spongycastle.util.encoders.Hex; - -import java.math.BigInteger; -import java.util.Arrays; - -import static com.tangem.wallet.ByteUtil.EMPTY_BYTE_ARRAY; - -/** - * Created by Ilia on 07.01.2018. - */ - -public class ETH_Transaction { - byte[] nonce; - byte[] gasPrice; - byte[] gasLimit; - byte[] receiveAddress; - byte[] value; - byte[] data; - Integer chainId; - byte[] rlpRaw; - public ECDSASignature_ETH signature; - byte[] rlpEncoded; - - private static final int CHAIN_ID_INC = 35; - private static final int LOWER_REAL_V = 27; - - public static ETH_Transaction create(String to, BigInteger amount, BigInteger nonce, BigInteger gasPrice, - BigInteger gasLimit, Integer chainId){ - return new ETH_Transaction(BigIntegers.asUnsignedByteArray(nonce), - BigIntegers.asUnsignedByteArray(gasPrice), - BigIntegers.asUnsignedByteArray(gasLimit), - Hex.decode(to), - BigIntegers.asUnsignedByteArray(amount), - null, - chainId); - } - - - public static ETH_Transaction create(String to, BigInteger amount, BigInteger nonce, BigInteger gasPrice, - BigInteger gasLimit, Integer chainId, byte[] data){ - return new ETH_Transaction(BigIntegers.asUnsignedByteArray(nonce), - BigIntegers.asUnsignedByteArray(gasPrice), - BigIntegers.asUnsignedByteArray(gasLimit), - Hex.decode(to), - BigIntegers.asUnsignedByteArray(amount), - data, - chainId); - } - - public ETH_Transaction(byte[] nonce, byte[] gasPrice, byte[] gasLimit, byte[] receiveAddress, byte[] value, byte[] data, - Integer chainId) { - this.nonce = nonce; - this.gasPrice = gasPrice; - this.gasLimit = gasLimit; - this.receiveAddress = receiveAddress; - if (ByteUtil.isSingleZero(value)) { - this.value = EMPTY_BYTE_ARRAY; - } else { - this.value = value; - } - this.data = data; - this.chainId = chainId; - - if (receiveAddress == null) { - this.receiveAddress = ByteUtil.EMPTY_BYTE_ARRAY; - } - } - - public enum ChainEnum - { - Mainnet(1), - Morden(2), - Ropsten(3), - Rinkeby(4), - Rootstock_mainnet(30), - Rootstock_testnet (31), - Kovan (42), - Ethereum_Classic_mainnet(61), - Ethereum_Classic_testnet(62), - Geth_private_chains (1337); - - private int value; - - ChainEnum(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - } - - public byte[] getRawHash() { - - byte[] plainMsg = this.getEncodedRaw(); - Keccak256 kec = new Keccak256(); - return kec.digest(plainMsg); - } - - public byte[] getHash() { - - byte[] plainMsg = this.getEncoded(); - Keccak256 kec = new Keccak256(); - return kec.digest(plainMsg); - } - - public int BruteRecoveryID2(ECDSASignature_ETH sig, byte[] messageHash, byte[] thisKey) - { - Log.e("ETH_KZ", BTCUtils.toHex(thisKey)); - int recId = -1; - for (int i = 0; i < 4; i++) { - byte[] recK = CryptoUtil.recoverPubBytesFromSignature(i, sig, messageHash); - - if(recK == null) - { - continue; - } - - Log.e("ETH_k "+String.valueOf(i), BTCUtils.toHex(recK)); - if (Arrays.equals(recK, thisKey)) { - recId = i; - recId +=27; - break; - } - } - return recId; - } - - public int BruteRecoveryID(ECKey.ECDSASignature sig, Sha256Hash messageHash, byte[] thisKey) - { - Log.e("ETH_KZ", BTCUtils.toHex(thisKey)); - int recId = -1; - for (int i = 0; i < 4; i++) { - ECKey k = ECKey.recoverFromSignature(i, sig, messageHash, false); - - if(k == null) - continue; - byte[] recK = k.getPubKey(); - Log.e("ETH_k "+String.valueOf(i), BTCUtils.toHex(recK)); - if (k != null && Arrays.equals(recK, thisKey)) { - recId = i; - break; - } - } - return recId; - } - - // signed TX - public byte[] getEncoded() { - - // parse null as 0 for nonce - byte[] nonce = null; - if (this.nonce == null || this.nonce.length == 1 && this.nonce[0] == 0) { - nonce = RLP.encodeElement(null); - } else { - nonce = RLP.encodeElement(this.nonce); - } - byte[] gasPrice = RLP.encodeElement(this.gasPrice); - byte[] gasLimit = RLP.encodeElement(this.gasLimit); - byte[] receiveAddress = RLP.encodeElement(this.receiveAddress); - byte[] value = RLP.encodeElement(this.value); - byte[] data = RLP.encodeElement(this.data); - - byte[] v, r, s; - - if (signature != null) { - int encodeV; - if (chainId == null) { - encodeV = signature.v; - } else { - encodeV = signature.v - LOWER_REAL_V; - encodeV += chainId * 2 + CHAIN_ID_INC; - } - v = RLP.encodeInt(encodeV); - r = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.r)); - s = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.s)); - } else { - // Since EIP-155 use chainId for v - v = chainId == null ? RLP.encodeElement(EMPTY_BYTE_ARRAY) : RLP.encodeInt(chainId); - r = RLP.encodeElement(EMPTY_BYTE_ARRAY); - s = RLP.encodeElement(EMPTY_BYTE_ARRAY); - } - - this.rlpEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, - receiveAddress, value, data, v, r, s); - - //this.hash = this.getHash(); - - return rlpEncoded; - } - - // unsigned TX - public byte[] getEncodedRaw() { - // parse null as 0 for nonce - byte[] nonce = null; - if (this.nonce == null || this.nonce.length == 1 && this.nonce[0] == 0) { - nonce = RLP.encodeElement(null); - } else { - nonce = RLP.encodeElement(this.nonce); - } - byte[] gasPrice = RLP.encodeElement(this.gasPrice); - byte[] gasLimit = RLP.encodeElement(this.gasLimit); - byte[] receiveAddress = RLP.encodeElement(this.receiveAddress); - byte[] value = RLP.encodeElement(this.value); - byte[] data = RLP.encodeElement(this.data); - - // Since EIP-155 use chainId for v - if (chainId == null) { - rlpRaw = RLP.encodeList(nonce, gasPrice, gasLimit, receiveAddress, - value, data); - } else { - byte[] v, r, s; - v = RLP.encodeInt(chainId); - r = RLP.encodeElement(EMPTY_BYTE_ARRAY); - s = RLP.encodeElement(EMPTY_BYTE_ARRAY); - rlpRaw = RLP.encodeList(nonce, gasPrice, gasLimit, receiveAddress, - value, data, v, r, s); - } - return rlpRaw; - } -} +package com.tangem.wallet; + + +import android.util.Log; + +import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.Sha256Hash; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.encoders.Hex; + +import java.math.BigInteger; +import java.util.Arrays; + +import static com.tangem.wallet.ByteUtil.EMPTY_BYTE_ARRAY; + +/** + * Created by Ilia on 07.01.2018. + */ + +public class ETH_Transaction { + byte[] nonce; + byte[] gasPrice; + byte[] gasLimit; + byte[] receiveAddress; + byte[] value; + byte[] data; + Integer chainId; + byte[] rlpRaw; + public ECDSASignature_ETH signature; + byte[] rlpEncoded; + + private static final int CHAIN_ID_INC = 35; + private static final int LOWER_REAL_V = 27; + + public static ETH_Transaction create(String to, BigInteger amount, BigInteger nonce, BigInteger gasPrice, + BigInteger gasLimit, Integer chainId){ + return new ETH_Transaction(BigIntegers.asUnsignedByteArray(nonce), + BigIntegers.asUnsignedByteArray(gasPrice), + BigIntegers.asUnsignedByteArray(gasLimit), + Hex.decode(to), + BigIntegers.asUnsignedByteArray(amount), + null, + chainId); + } + + + public static ETH_Transaction create(String to, BigInteger amount, BigInteger nonce, BigInteger gasPrice, + BigInteger gasLimit, Integer chainId, byte[] data){ + return new ETH_Transaction(BigIntegers.asUnsignedByteArray(nonce), + BigIntegers.asUnsignedByteArray(gasPrice), + BigIntegers.asUnsignedByteArray(gasLimit), + Hex.decode(to), + BigIntegers.asUnsignedByteArray(amount), + data, + chainId); + } + + public ETH_Transaction(byte[] nonce, byte[] gasPrice, byte[] gasLimit, byte[] receiveAddress, byte[] value, byte[] data, + Integer chainId) { + this.nonce = nonce; + this.gasPrice = gasPrice; + this.gasLimit = gasLimit; + this.receiveAddress = receiveAddress; + if (ByteUtil.isSingleZero(value)) { + this.value = EMPTY_BYTE_ARRAY; + } else { + this.value = value; + } + this.data = data; + this.chainId = chainId; + + if (receiveAddress == null) { + this.receiveAddress = ByteUtil.EMPTY_BYTE_ARRAY; + } + } + + public enum ChainEnum + { + Mainnet(1), + Morden(2), + Ropsten(3), + Rinkeby(4), + Rootstock_mainnet(30), + Rootstock_testnet (31), + Kovan (42), + Ethereum_Classic_mainnet(61), + Ethereum_Classic_testnet(62), + Geth_private_chains (1337); + + private int value; + + ChainEnum(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + public byte[] getRawHash() { + + byte[] plainMsg = this.getEncodedRaw(); + Keccak256 kec = new Keccak256(); + return kec.digest(plainMsg); + } + + public byte[] getHash() { + + byte[] plainMsg = this.getEncoded(); + Keccak256 kec = new Keccak256(); + return kec.digest(plainMsg); + } + + public int BruteRecoveryID2(ECDSASignature_ETH sig, byte[] messageHash, byte[] thisKey) + { + Log.e("ETH_KZ", BTCUtils.toHex(thisKey)); + int recId = -1; + for (int i = 0; i < 4; i++) { + byte[] recK = CryptoUtil.recoverPubBytesFromSignature(i, sig, messageHash); + + if(recK == null) + { + continue; + } + + Log.e("ETH_k "+String.valueOf(i), BTCUtils.toHex(recK)); + if (Arrays.equals(recK, thisKey)) { + recId = i; + recId +=27; + break; + } + } + return recId; + } + + public int BruteRecoveryID(ECKey.ECDSASignature sig, Sha256Hash messageHash, byte[] thisKey) + { + Log.e("ETH_KZ", BTCUtils.toHex(thisKey)); + int recId = -1; + for (int i = 0; i < 4; i++) { + ECKey k = ECKey.recoverFromSignature(i, sig, messageHash, false); + + if(k == null) + continue; + byte[] recK = k.getPubKey(); + Log.e("ETH_k "+String.valueOf(i), BTCUtils.toHex(recK)); + if (k != null && Arrays.equals(recK, thisKey)) { + recId = i; + break; + } + } + return recId; + } + + // signed TX + public byte[] getEncoded() { + + // parse null as 0 for nonce + byte[] nonce = null; + if (this.nonce == null || this.nonce.length == 1 && this.nonce[0] == 0) { + nonce = RLP.encodeElement(null); + } else { + nonce = RLP.encodeElement(this.nonce); + } + byte[] gasPrice = RLP.encodeElement(this.gasPrice); + byte[] gasLimit = RLP.encodeElement(this.gasLimit); + byte[] receiveAddress = RLP.encodeElement(this.receiveAddress); + byte[] value = RLP.encodeElement(this.value); + byte[] data = RLP.encodeElement(this.data); + + byte[] v, r, s; + + if (signature != null) { + int encodeV; + if (chainId == null) { + encodeV = signature.v; + } else { + encodeV = signature.v - LOWER_REAL_V; + encodeV += chainId * 2 + CHAIN_ID_INC; + } + v = RLP.encodeInt(encodeV); + r = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.r)); + s = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.s)); + } else { + // Since EIP-155 use chainId for v + v = chainId == null ? RLP.encodeElement(EMPTY_BYTE_ARRAY) : RLP.encodeInt(chainId); + r = RLP.encodeElement(EMPTY_BYTE_ARRAY); + s = RLP.encodeElement(EMPTY_BYTE_ARRAY); + } + + this.rlpEncoded = RLP.encodeList(nonce, gasPrice, gasLimit, + receiveAddress, value, data, v, r, s); + + //this.hash = this.getHash(); + + return rlpEncoded; + } + + // unsigned TX + public byte[] getEncodedRaw() { + // parse null as 0 for nonce + byte[] nonce = null; + if (this.nonce == null || this.nonce.length == 1 && this.nonce[0] == 0) { + nonce = RLP.encodeElement(null); + } else { + nonce = RLP.encodeElement(this.nonce); + } + byte[] gasPrice = RLP.encodeElement(this.gasPrice); + byte[] gasLimit = RLP.encodeElement(this.gasLimit); + byte[] receiveAddress = RLP.encodeElement(this.receiveAddress); + byte[] value = RLP.encodeElement(this.value); + byte[] data = RLP.encodeElement(this.data); + + // Since EIP-155 use chainId for v + if (chainId == null) { + rlpRaw = RLP.encodeList(nonce, gasPrice, gasLimit, receiveAddress, + value, data); + } else { + byte[] v, r, s; + v = RLP.encodeInt(chainId); + r = RLP.encodeElement(EMPTY_BYTE_ARRAY); + s = RLP.encodeElement(EMPTY_BYTE_ARRAY); + rlpRaw = RLP.encodeList(nonce, gasPrice, gasLimit, receiveAddress, + value, data, v, r, s); + } + return rlpRaw; + } +} diff --git a/app/src/main/java/com/tangem/wallet/Electrum_Request.java b/app/src/main/java/com/tangem/wallet/Electrum_Request.java index ae5a34cbde..9654c7fb81 100644 --- a/app/src/main/java/com/tangem/wallet/Electrum_Request.java +++ b/app/src/main/java/com/tangem/wallet/Electrum_Request.java @@ -1,184 +1,184 @@ -package com.tangem.wallet; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Created by dvol on 16.07.2017. - */ - -public class Electrum_Request { - public static final String METHOD_GetBalance = "blockchain.address.get_balance"; - public static final String METHOD_ListUnspent = "blockchain.address.listunspent"; - public static final String METHOD_GetHistory = "blockchain.address.get_history"; - public static final String METHOD_GetTransaction = "blockchain.transaction.get"; - public static final String METHOD_GetHeader = "blockchain.block.get_header"; - public static final String METHOD_SendTransaction = "blockchain.transaction.broadcast"; - public static final String METHOD_GetFee = "blockchain.estimatefee"; - - - - public JSONObject jsRequestData; - public String answerData; - public String error; - public String WalletAddress; - public String TxHash; - public String Host; - public int Port; - - private Electrum_Request() { - } - - public Electrum_Request(JSONObject jsRequest) { - try { - jsRequestData = new JSONObject(jsRequest.toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public JSONObject getAnswer() { - try { - return new JSONObject(answerData); - } catch (Exception e) { - try { - return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage())); - } catch (JSONException e1) { - e1.printStackTrace(); - return null; - } - } - } - - public String getAsString() { - return jsRequestData.toString(); - } - - public void setID(int value) { - try { - jsRequestData.put("id", String.format("%d", value)); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public int getID() { - try { - return jsRequestData.getInt("id"); - } catch (JSONException e) { - e.printStackTrace(); - return 0; - } - } - - public static Electrum_Request CheckBalance(String wallet) { - Electrum_Request request = new Electrum_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetBalance + "\", \"params\":[\"" + wallet + "\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - - public static Electrum_Request GetFee(String wallet) { - Electrum_Request request = new Electrum_Request(); - try{ - request.WalletAddress = wallet; //METHOD_GetFee - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetFee + "\", \"params\":[\"" + 6 + "\"] }"); - } - catch(JSONException e) - { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Electrum_Request GetHeader(String wallet, String height) { - Electrum_Request request = new Electrum_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetHeader + "\", \"params\":[\"" + height + "\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Electrum_Request ListUnspent(String wallet) { - Electrum_Request request = new Electrum_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ListUnspent + "\", \"params\":[\"" + wallet + "\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Electrum_Request Broadcast(String wallet, String tx) { - Electrum_Request request = new Electrum_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_SendTransaction + "\", \"params\":[\"" + tx + "\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Electrum_Request ListHistory(String wallet) { - Electrum_Request request = new Electrum_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetHistory + "\", \"params\":[\"" + wallet + "\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Electrum_Request GetTransaction(String wallet, String tx_hash) { - Electrum_Request request = new Electrum_Request(); - try { - request.WalletAddress=wallet; - request.TxHash = tx_hash; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetTransaction + "\", \"params\":[\"" + tx_hash + "\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public boolean isMethod(String methodName) throws JSONException { - return jsRequestData.getString("method").equals(methodName); - } - - public JSONArray getParams() throws JSONException { - return jsRequestData.getJSONArray("params"); - } - - public JSONObject getResult() throws JSONException { - return getAnswer().getJSONObject("result"); - } - - public String getResultString() throws JSONException { - return getAnswer().getString("result"); - } - - public JSONArray getResultArray() throws JSONException { - return getAnswer().getJSONArray("result"); - } - - -} +package com.tangem.wallet; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Created by dvol on 16.07.2017. + */ + +public class Electrum_Request { + public static final String METHOD_GetBalance = "blockchain.address.get_balance"; + public static final String METHOD_ListUnspent = "blockchain.address.listunspent"; + public static final String METHOD_GetHistory = "blockchain.address.get_history"; + public static final String METHOD_GetTransaction = "blockchain.transaction.get"; + public static final String METHOD_GetHeader = "blockchain.block.get_header"; + public static final String METHOD_SendTransaction = "blockchain.transaction.broadcast"; + public static final String METHOD_GetFee = "blockchain.estimatefee"; + + + + public JSONObject jsRequestData; + public String answerData; + public String error; + public String WalletAddress; + public String TxHash; + public String Host; + public int Port; + + private Electrum_Request() { + } + + public Electrum_Request(JSONObject jsRequest) { + try { + jsRequestData = new JSONObject(jsRequest.toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public JSONObject getAnswer() { + try { + return new JSONObject(answerData); + } catch (Exception e) { + try { + return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage())); + } catch (JSONException e1) { + e1.printStackTrace(); + return null; + } + } + } + + public String getAsString() { + return jsRequestData.toString(); + } + + public void setID(int value) { + try { + jsRequestData.put("id", String.format("%d", value)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public int getID() { + try { + return jsRequestData.getInt("id"); + } catch (JSONException e) { + e.printStackTrace(); + return 0; + } + } + + public static Electrum_Request CheckBalance(String wallet) { + Electrum_Request request = new Electrum_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetBalance + "\", \"params\":[\"" + wallet + "\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + + public static Electrum_Request GetFee(String wallet) { + Electrum_Request request = new Electrum_Request(); + try{ + request.WalletAddress = wallet; //METHOD_GetFee + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetFee + "\", \"params\":[\"" + 6 + "\"] }"); + } + catch(JSONException e) + { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Electrum_Request GetHeader(String wallet, String height) { + Electrum_Request request = new Electrum_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetHeader + "\", \"params\":[\"" + height + "\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Electrum_Request ListUnspent(String wallet) { + Electrum_Request request = new Electrum_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ListUnspent + "\", \"params\":[\"" + wallet + "\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Electrum_Request Broadcast(String wallet, String tx) { + Electrum_Request request = new Electrum_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_SendTransaction + "\", \"params\":[\"" + tx + "\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Electrum_Request ListHistory(String wallet) { + Electrum_Request request = new Electrum_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetHistory + "\", \"params\":[\"" + wallet + "\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Electrum_Request GetTransaction(String wallet, String tx_hash) { + Electrum_Request request = new Electrum_Request(); + try { + request.WalletAddress=wallet; + request.TxHash = tx_hash; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_GetTransaction + "\", \"params\":[\"" + tx_hash + "\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public boolean isMethod(String methodName) throws JSONException { + return jsRequestData.getString("method").equals(methodName); + } + + public JSONArray getParams() throws JSONException { + return jsRequestData.getJSONArray("params"); + } + + public JSONObject getResult() throws JSONException { + return getAnswer().getJSONObject("result"); + } + + public String getResultString() throws JSONException { + return getAnswer().getString("result"); + } + + public JSONArray getResultArray() throws JSONException { + return getAnswer().getJSONArray("result"); + } + + +} diff --git a/app/src/main/java/com/tangem/wallet/Electrum_Task.java b/app/src/main/java/com/tangem/wallet/Electrum_Task.java index ec9512a2a5..2475277d68 100644 --- a/app/src/main/java/com/tangem/wallet/Electrum_Task.java +++ b/app/src/main/java/com/tangem/wallet/Electrum_Task.java @@ -1,116 +1,116 @@ -package com.tangem.wallet; - -import android.os.AsyncTask; -import android.util.Log; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.net.InetAddress; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by dvol on 16.07.2017. - */ - -public class Electrum_Task extends AsyncTask> { - public static final String logTag = "Electrum"; - //public static final String Host = /*"hsmiths.changeip.net";*/ "testnetnode.arihanc.com"; - //public static final int Port = /*8080*/51001; - private int reqID = 1; - private String Host = ""; - private int Port = 0; - OutputStreamWriter out; - BufferedReader in; - - SharedData sharedCounter = null; - - - public Electrum_Task(String host, int port) { - super(); - Host = host; - Port = port; - } - - public Electrum_Task(String host, int port, SharedData sharedCounter) { - super(); - Host = host; - Port = port; - this.sharedCounter = sharedCounter; - } - - @Override - protected List doInBackground(Electrum_Request... requests) { - List result = new ArrayList<>(); - for (int i = 0; i < requests.length; i++) { - result.add(requests[i]); - } - try { - - InetAddress serverAddress = InetAddress.getByName(Host); - Log.v(logTag, "Connecting..."+Host); - Socket socket = new Socket(serverAddress, Port); - socket.setSoTimeout(5000); - try { - OutputStream os = socket.getOutputStream(); - out = new OutputStreamWriter(os, "UTF-8"); - Log.v(logTag, "Connected"); - InputStream is = socket.getInputStream(); - in = new BufferedReader(new InputStreamReader(is)); - - publishProgress(5); - - for (int i = 0; i < requests.length; i++) { - requests[i].setID(reqID++); - doRequest(requests[i]); - publishProgress(5 + 90 * (i + 1) / requests.length); - } - - publishProgress(100); - - } catch (Exception e) { - Log.e(logTag, "Error: ", e); - } finally { - socket.close(); - } - } catch (Exception e) { - Log.e(logTag, "Error: ", e); - for (int i = 0; i < requests.length; i++) { - result.get(i).error = e.toString(); - } - } - return result; - } - - private void doRequest(Electrum_Request request) { - try { - - Log.v(logTag, "<< " + request.getAsString()); - - out.write(request.getAsString() + "\n"); - out.flush(); - - request.answerData = in.readLine(); - request.Host=Host; - request.Port=Port; - if (request.answerData != null) { - Log.v(logTag, ">> " + request.answerData); - } else { - request.error = "No answer from server"; - Log.v(logTag, ">> "); - } - } catch (Exception e) { - request.error = e.toString(); - } - } - - public String getValidationNodeDescription() { - return "Electrum, "+Host+":"+String.valueOf(Port); - } - - -} +package com.tangem.wallet; + +import android.os.AsyncTask; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.InetAddress; +import java.net.Socket; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by dvol on 16.07.2017. + */ + +public class Electrum_Task extends AsyncTask> { + public static final String logTag = "Electrum"; + //public static final String Host = /*"hsmiths.changeip.net";*/ "testnetnode.arihanc.com"; + //public static final int Port = /*8080*/51001; + private int reqID = 1; + private String Host = ""; + private int Port = 0; + OutputStreamWriter out; + BufferedReader in; + + SharedData sharedCounter = null; + + + public Electrum_Task(String host, int port) { + super(); + Host = host; + Port = port; + } + + public Electrum_Task(String host, int port, SharedData sharedCounter) { + super(); + Host = host; + Port = port; + this.sharedCounter = sharedCounter; + } + + @Override + protected List doInBackground(Electrum_Request... requests) { + List result = new ArrayList<>(); + for (int i = 0; i < requests.length; i++) { + result.add(requests[i]); + } + try { + + InetAddress serverAddress = InetAddress.getByName(Host); + Log.v(logTag, "Connecting..."+Host); + Socket socket = new Socket(serverAddress, Port); + socket.setSoTimeout(5000); + try { + OutputStream os = socket.getOutputStream(); + out = new OutputStreamWriter(os, "UTF-8"); + Log.v(logTag, "Connected"); + InputStream is = socket.getInputStream(); + in = new BufferedReader(new InputStreamReader(is)); + + publishProgress(5); + + for (int i = 0; i < requests.length; i++) { + requests[i].setID(reqID++); + doRequest(requests[i]); + publishProgress(5 + 90 * (i + 1) / requests.length); + } + + publishProgress(100); + + } catch (Exception e) { + Log.e(logTag, "Error: ", e); + } finally { + socket.close(); + } + } catch (Exception e) { + Log.e(logTag, "Error: ", e); + for (int i = 0; i < requests.length; i++) { + result.get(i).error = e.toString(); + } + } + return result; + } + + private void doRequest(Electrum_Request request) { + try { + + Log.v(logTag, "<< " + request.getAsString()); + + out.write(request.getAsString() + "\n"); + out.flush(); + + request.answerData = in.readLine(); + request.Host=Host; + request.Port=Port; + if (request.answerData != null) { + Log.v(logTag, ">> " + request.answerData); + } else { + request.error = "No answer from server"; + Log.v(logTag, ">> "); + } + } catch (Exception e) { + request.error = e.toString(); + } + } + + public String getValidationNodeDescription() { + return "Electrum, "+Host+":"+String.valueOf(Port); + } + + +} diff --git a/app/src/main/java/com/tangem/wallet/EmptyWalletActivity.java b/app/src/main/java/com/tangem/wallet/EmptyWalletActivity.java index 4413e2844b..4b5db2dc7a 100644 --- a/app/src/main/java/com/tangem/wallet/EmptyWalletActivity.java +++ b/app/src/main/java/com/tangem/wallet/EmptyWalletActivity.java @@ -1,356 +1,356 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.IsoDep; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.view.View; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.NfcManager; -import com.tangem.cardReader.Util; - -public class EmptyWalletActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications { - - private static final int REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY = 2; - private static final int REQUEST_CODE_REQUEST_PIN2 = 3; - private static final int REQUEST_CODE_VERIFY_CARD = 4; - Tangem_Card mCard; - TextView tvCardID, tvIssuer, tvIssuerData, tvBlockchain; - ProgressBar progressBar; - ImageView ivBlockchain, ivPIN, ivPIN2orSecurityDelay, ivDeveloperVersion; - - private NfcManager mNfcManager; - private final String logTag = "EmptyWalletActivity"; - private boolean lastReadSuccess = true; - private VerifyCardTask verifyCardTask = null; - private int requestPIN2Count = 0; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_empty_wallet); - - MainActivity.commonInit(getApplicationContext()); - mNfcManager = new NfcManager(this, this); - - mCard = new Tangem_Card(getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); - - tvCardID = findViewById(R.id.tvCardID); - tvCardID.setText(mCard.getCIDDescription()); - - tvIssuer = findViewById(R.id.tvIssuer); - tvIssuerData = findViewById(R.id.tvIssuerData); - tvBlockchain = findViewById(R.id.tvBlockchain); - - tvIssuer.setText(mCard.getIssuerDescription()); - tvIssuerData.setText(mCard.getIssuerDataDescription()); - - //tvBlockchain.setText(mCard.getBlockchain().getOfficialName()); - tvBlockchain.setText(mCard.getBlockchainName()); - progressBar = findViewById(R.id.progressBar); - - ivBlockchain = findViewById(R.id.imgBlockchain); - ivPIN = findViewById(R.id.imgPIN); - ivPIN2orSecurityDelay = findViewById(R.id.imgPIN2orSecurityDelay); - ivDeveloperVersion = findViewById(R.id.imgDeveloperVersion); - - ivBlockchain.setImageResource(mCard.getBlockchain().getImageResource(this, mCard.getTokenSymbol())); - - if (mCard.useDefaultPIN1()) { - ivPIN.setImageResource(R.drawable.unlock_pin1); - ivPIN.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by default PIN1 code", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivPIN.setImageResource(R.drawable.lock_pin1); - ivPIN.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by user's PIN1 code", Toast.LENGTH_LONG).show(); - } - }); - } - - if (mCard.getPauseBeforePIN2() > 0 && (mCard.useDefaultPIN2() || !mCard.useSmartSecurityDelay())) { - ivPIN2orSecurityDelay.setImageResource(R.drawable.timer); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(EmptyWalletActivity.this, String.format("This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code", mCard.getPauseBeforePIN2() / 1000.0), Toast.LENGTH_LONG).show(); - } - }); - - } else if (mCard.useDefaultPIN2()) { - ivPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by default PIN2 code", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by user's PIN2 code", Toast.LENGTH_LONG).show(); - } - }); - } - - - if (mCard.useDevelopersFirmware()) { - ivDeveloperVersion.setImageResource(R.drawable.ic_developer_version); - ivDeveloperVersion.setVisibility(View.VISIBLE); - ivDeveloperVersion.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(EmptyWalletActivity.this, "Unlocked banknote, only for development use", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivDeveloperVersion.setVisibility(View.INVISIBLE); - } - - Button btnNewWallet = findViewById(R.id.btnNewWallet); - btnNewWallet.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - //CreateSelectBlockchainDialog(); - requestPIN2Count = 0; - Intent intent = new Intent(getBaseContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2); - } - }); - - if (getIntent().getExtras().containsKey(NfcAdapter.EXTRA_TAG)) { - Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag != null) { - onTagDiscovered(tag); - } - } - } - - - private void doCreateNewWallet() { - Intent intent = new Intent(this, CreateNewWalletActivity.class); - - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - -// intent.putExtra("newPIN",mCard.getPIN()); -// intent.putExtra("newPIN2","12345678"); - startActivityForResult(intent, REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY) { - if (resultCode == Activity.RESULT_OK) { - - if (data != null) { - data.putExtra("modification", "updateAndViewCard"); - data.putExtra("updateDelay", 0); - setResult(Activity.RESULT_OK, data); - } - finish(); - } else { - if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { - Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID")); - updatedCard.LoadFromBundle(data.getBundleExtra("Card")); - mCard = updatedCard; - } - if (resultCode == CreateNewWalletActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) { - requestPIN2Count++; - Intent intent = new Intent(getBaseContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2); - return; - } - } - setResult(resultCode, data); - finish(); - } else if (requestCode == REQUEST_CODE_REQUEST_PIN2) { - if (resultCode == Activity.RESULT_OK) { - doCreateNewWallet(); - } - } - - } - - @Override - public void onTagDiscovered(Tag tag) { - try { - final IsoDep isoDep = IsoDep.get(tag); - if (isoDep == null) { - throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); - } - byte UID[] = tag.getId(); - String sUID = Util.byteArrayToHexString(UID); - if (!mCard.getUID().equals(sUID)) { - Log.d(logTag, "Invalid UID: " + sUID); - mNfcManager.IgnoreTag(isoDep.getTag()); - return; - } else { - Log.v(logTag, "UID: " + sUID); - } - - if (lastReadSuccess) { - isoDep.setTimeout(1000); - } else { - isoDep.setTimeout(65000); - } - //lastTag = tag; - verifyCardTask = new VerifyCardTask(this, mCard, mNfcManager, isoDep, this); - verifyCardTask.start(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void OnReadStart(CardProtocol cardProtocol) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setVisibility(View.VISIBLE); - progressBar.setProgress(5); - } - }); - } - - public void OnReadFinish(final CardProtocol cardProtocol) { - - verifyCardTask = null; - - if (cardProtocol != null) { - if (cardProtocol.getError() == null) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); - Intent intent = new Intent(EmptyWalletActivity.this, VerifyCardActivity.class); - // TODO обновить карту mCard - intent.putExtra("UID", cardProtocol.getCard().getUID()); - intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_VERIFY_CARD); - //addCard(cardProtocol.getCard()); - } - }); - } else { - // remove last UIDs because of error and no card read - progressBar.post(new Runnable() { - @Override - public void run() { - lastReadSuccess = false; - if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { - if (!NoExtendedLengthSupportDialog.allreadyShowed) { - new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog"); - } - } else { - Toast.makeText(EmptyWalletActivity.this, "Try to scan again", Toast.LENGTH_LONG).show(); - } - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - } - }); - } - } - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - public void OnReadProgress(CardProtocol protocol, final int progress) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(progress); - } - }); - } - - public void OnReadCancel() { - - verifyCardTask = null; - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - @Override - public void OnReadWait(int msec) { - WaitSecurityDelayDialog.OnReadWait(this, msec); - } - - @Override - public void OnReadBeforeRequest(int timeout) { - WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout); - } - - @Override - public void OnReadAfterRequest() { - WaitSecurityDelayDialog.onReadAfterRequest(this); - } - - - @Override - public void onResume() { - super.onResume(); - mNfcManager.onResume(); - } - - @Override - public void onPause() { - super.onPause(); - mNfcManager.onPause(); - } - - @Override - public void onStop() { - super.onStop(); - mNfcManager.onStop(); - } -} +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.NfcManager; +import com.tangem.cardReader.Util; + +public class EmptyWalletActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications { + + private static final int REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY = 2; + private static final int REQUEST_CODE_REQUEST_PIN2 = 3; + private static final int REQUEST_CODE_VERIFY_CARD = 4; + Tangem_Card mCard; + TextView tvCardID, tvIssuer, tvIssuerData, tvBlockchain; + ProgressBar progressBar; + ImageView ivBlockchain, ivPIN, ivPIN2orSecurityDelay, ivDeveloperVersion; + + private NfcManager mNfcManager; + private final String logTag = "EmptyWalletActivity"; + private boolean lastReadSuccess = true; + private VerifyCardTask verifyCardTask = null; + private int requestPIN2Count = 0; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_empty_wallet); + + MainActivity.commonInit(getApplicationContext()); + mNfcManager = new NfcManager(this, this); + + mCard = new Tangem_Card(getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); + + tvCardID = findViewById(R.id.tvCardID); + tvCardID.setText(mCard.getCIDDescription()); + + tvIssuer = findViewById(R.id.tvIssuer); + tvIssuerData = findViewById(R.id.tvIssuerData); + tvBlockchain = findViewById(R.id.tvBlockchain); + + tvIssuer.setText(mCard.getIssuerDescription()); + tvIssuerData.setText(mCard.getIssuerDataDescription()); + + //tvBlockchain.setText(mCard.getBlockchain().getOfficialName()); + tvBlockchain.setText(mCard.getBlockchainName()); + progressBar = findViewById(R.id.progressBar); + + ivBlockchain = findViewById(R.id.imgBlockchain); + ivPIN = findViewById(R.id.imgPIN); + ivPIN2orSecurityDelay = findViewById(R.id.imgPIN2orSecurityDelay); + ivDeveloperVersion = findViewById(R.id.imgDeveloperVersion); + + ivBlockchain.setImageResource(mCard.getBlockchain().getImageResource(this, mCard.getTokenSymbol())); + + if (mCard.useDefaultPIN1()) { + ivPIN.setImageResource(R.drawable.unlock_pin1); + ivPIN.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by default PIN1 code", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivPIN.setImageResource(R.drawable.lock_pin1); + ivPIN.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by user's PIN1 code", Toast.LENGTH_LONG).show(); + } + }); + } + + if (mCard.getPauseBeforePIN2() > 0 && (mCard.useDefaultPIN2() || !mCard.useSmartSecurityDelay())) { + ivPIN2orSecurityDelay.setImageResource(R.drawable.timer); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(EmptyWalletActivity.this, String.format("This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code", mCard.getPauseBeforePIN2() / 1000.0), Toast.LENGTH_LONG).show(); + } + }); + + } else if (mCard.useDefaultPIN2()) { + ivPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by default PIN2 code", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by user's PIN2 code", Toast.LENGTH_LONG).show(); + } + }); + } + + + if (mCard.useDevelopersFirmware()) { + ivDeveloperVersion.setImageResource(R.drawable.ic_developer_version); + ivDeveloperVersion.setVisibility(View.VISIBLE); + ivDeveloperVersion.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(EmptyWalletActivity.this, "Unlocked banknote, only for development use", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivDeveloperVersion.setVisibility(View.INVISIBLE); + } + + Button btnNewWallet = findViewById(R.id.btnNewWallet); + btnNewWallet.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + //CreateSelectBlockchainDialog(); + requestPIN2Count = 0; + Intent intent = new Intent(getBaseContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2); + } + }); + + if (getIntent().getExtras().containsKey(NfcAdapter.EXTRA_TAG)) { + Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag != null) { + onTagDiscovered(tag); + } + } + } + + + private void doCreateNewWallet() { + Intent intent = new Intent(this, CreateNewWalletActivity.class); + + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + +// intent.putExtra("newPIN",mCard.getPIN()); +// intent.putExtra("newPIN2","12345678"); + startActivityForResult(intent, REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY) { + if (resultCode == Activity.RESULT_OK) { + + if (data != null) { + data.putExtra("modification", "updateAndViewCard"); + data.putExtra("updateDelay", 0); + setResult(Activity.RESULT_OK, data); + } + finish(); + } else { + if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { + Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID")); + updatedCard.LoadFromBundle(data.getBundleExtra("Card")); + mCard = updatedCard; + } + if (resultCode == CreateNewWalletActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) { + requestPIN2Count++; + Intent intent = new Intent(getBaseContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2); + return; + } + } + setResult(resultCode, data); + finish(); + } else if (requestCode == REQUEST_CODE_REQUEST_PIN2) { + if (resultCode == Activity.RESULT_OK) { + doCreateNewWallet(); + } + } + + } + + @Override + public void onTagDiscovered(Tag tag) { + try { + final IsoDep isoDep = IsoDep.get(tag); + if (isoDep == null) { + throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); + } + byte UID[] = tag.getId(); + String sUID = Util.byteArrayToHexString(UID); + if (!mCard.getUID().equals(sUID)) { + Log.d(logTag, "Invalid UID: " + sUID); + mNfcManager.IgnoreTag(isoDep.getTag()); + return; + } else { + Log.v(logTag, "UID: " + sUID); + } + + if (lastReadSuccess) { + isoDep.setTimeout(1000); + } else { + isoDep.setTimeout(65000); + } + //lastTag = tag; + verifyCardTask = new VerifyCardTask(this, mCard, mNfcManager, isoDep, this); + verifyCardTask.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void OnReadStart(CardProtocol cardProtocol) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setVisibility(View.VISIBLE); + progressBar.setProgress(5); + } + }); + } + + public void OnReadFinish(final CardProtocol cardProtocol) { + + verifyCardTask = null; + + if (cardProtocol != null) { + if (cardProtocol.getError() == null) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); + Intent intent = new Intent(EmptyWalletActivity.this, VerifyCardActivity.class); + // TODO обновить карту mCard + intent.putExtra("UID", cardProtocol.getCard().getUID()); + intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_VERIFY_CARD); + //addCard(cardProtocol.getCard()); + } + }); + } else { + // remove last UIDs because of error and no card read + progressBar.post(new Runnable() { + @Override + public void run() { + lastReadSuccess = false; + if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { + if (!NoExtendedLengthSupportDialog.allreadyShowed) { + new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog"); + } + } else { + Toast.makeText(EmptyWalletActivity.this, "Try to scan again", Toast.LENGTH_LONG).show(); + } + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + } + }); + } + } + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + public void OnReadProgress(CardProtocol protocol, final int progress) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(progress); + } + }); + } + + public void OnReadCancel() { + + verifyCardTask = null; + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + @Override + public void OnReadWait(int msec) { + WaitSecurityDelayDialog.OnReadWait(this, msec); + } + + @Override + public void OnReadBeforeRequest(int timeout) { + WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout); + } + + @Override + public void OnReadAfterRequest() { + WaitSecurityDelayDialog.onReadAfterRequest(this); + } + + + @Override + public void onResume() { + super.onResume(); + mNfcManager.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mNfcManager.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + mNfcManager.onStop(); + } +} diff --git a/app/src/main/java/com/tangem/wallet/EthEngine.java b/app/src/main/java/com/tangem/wallet/EthEngine.java index 56be142acc..780339247f 100644 --- a/app/src/main/java/com/tangem/wallet/EthEngine.java +++ b/app/src/main/java/com/tangem/wallet/EthEngine.java @@ -1,392 +1,392 @@ -package com.tangem.wallet; - -import android.net.Uri; -import android.util.Log; - -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.TLV; - -import org.bitcoinj.core.ECKey; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.Date; - -import static com.tangem.wallet.FormatUtil.GetDecimalFormat; - -/** - * Created by Ilia on 15.02.2018. - */ - -public class EthEngine extends CoinEngine{ - - public String GetNextNode(Tangem_Card mCard) - { - return "abc1.hsmiths.com"; - } - - public int GetNextNodePort(Tangem_Card mCard) - { - return 60001; - } - - public String GetNode(Tangem_Card mCard) - { - return "abc1.hsmiths.com"; - } - - public int GetNodePort(Tangem_Card mCard) - { - return 60001; - } - - public void SwitchNode(Tangem_Card mCard) - { - } - - public boolean InOutPutVisible() - { - return false; - } - - public String GetBalanceCurrency(Tangem_Card card) - { - return "ETH"; - } - - public boolean AwaitingConfirmation(Tangem_Card card) - { - return false; - } - - public String GetFeeCurrency() - { - return "Gwei"; - } - - public boolean IsNeedCheckNode() - { - return false; - } - - BigDecimal convertToEth(String value) - { - BigInteger m = new BigInteger(value, 10); - BigDecimal n = new BigDecimal(m); - BigDecimal d = n.divide(new BigDecimal("1000000000000000000")); - d = d.setScale(8, RoundingMode.DOWN); - return d; - } - - public int GetTokenDecimals(Tangem_Card card) - { - return 0; - } - - public String GetContractAddress(Tangem_Card card) - { - return ""; - } - - public boolean ValdateAddress(String address, Tangem_Card card) { - - if (address == null || address.isEmpty()) - { - return false; - } - - if(!address.startsWith("0x")&&!address.startsWith("0X")) - { - return false; - } - - if(address.length()!=42) - { - return false; - } - - return true; - } - public String GetBalanceValue(Tangem_Card mCard) - { - String dec = mCard.getDecimalBalance(); - BigDecimal d = convertToEth(dec); - String s = d.toString(); - - String pattern = "#0.000"; // If you like 4 zeros - DecimalFormat myFormatter = new DecimalFormat(pattern); - String output = myFormatter.format(d); - return output; - } - - public static String getAmountEquivalentDescriptionETH(BigDecimal amount, float rateValue) { - if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0) - return ""; - - if (rateValue > 0) { - BigDecimal biRate = new BigDecimal(rateValue); - BigDecimal exchangeCurs = biRate.multiply(amount); - exchangeCurs = exchangeCurs.setScale(2, RoundingMode.DOWN); - return "≈ USD  " + exchangeCurs.toString(); - } else { - return "≈ USD  ---"; - } - } - - public static String getAmountEquivalentDescriptionETH(Double amount, float rate) { - if (amount == 0) - return ""; - amount = amount / 100000; - if (rate > 0) { - return String.format("≈ USD %.2f", amount * rate); - } else { - return "≈ USD  ---"; - } - } - - - - @Override - public String GetBalanceEquivalent(Tangem_Card mCard) { - String dec = mCard.getDecimalBalance(); - BigDecimal d = convertToEth(dec); - return getAmountEquivalentDescriptionETH(d, mCard.getRate()); - } - - @Override - public String GetBalance(Tangem_Card mCard) { - if(!HasBalanceInfo(mCard)){ - return "-- -- -- " + GetBalanceCurrency(mCard); - } - - String output = GetBalanceValue(mCard); - String s = output + " " + GetBalanceCurrency(mCard); - return s; - } - - public Long GetBalanceLong(Tangem_Card mCard) - { - return mCard.getBalance(); - } - - public String GetBalanceWithAlter(Tangem_Card mCard) - { - return GetBalance(mCard); - } - - public boolean IsBalanceAlterNotZero(Tangem_Card card) - { - return true; - } - - public boolean IsBalanceNotZero(Tangem_Card card) - { - String balance = card.getDecimalBalance(); - if(balance == null || balance == "") - return false; - - BigDecimal bi = new BigDecimal(balance); - - if (BigDecimal.ZERO.compareTo(bi) == 0) - return false; - - return true; - } - - @Override - public String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception { - throw new Exception("Not implemented"); - } - - @Override - public byte[] ConvertAmountToByteArray(Tangem_Card mCard, String amount) throws Exception { - throw new Exception("Not implemented"); - } - - @Override - public String GetAmountDescription(Tangem_Card mCard, String amount) throws Exception { - throw new Exception("Not implemented"); - } - - public String GetAmountEqualentDescriptor(Tangem_Card mCard, String value) - { - BigDecimal d = new BigDecimal(value); - return getAmountEquivalentDescriptionETH(d, mCard.getRate()); - } - - public boolean CheckAmount(Tangem_Card card, String amount) throws Exception - { - DecimalFormat decimalFormat = GetDecimalFormat(); - BigDecimal amountValue = (BigDecimal) decimalFormat.parse(amount); //new BigDecimal(strAmount); - BigDecimal maxValue = new BigDecimal(GetBalanceValue(card)); - if(amountValue.compareTo(maxValue) > 0 ) - { - return false; - } - - return true; - } - - public boolean HasBalanceInfo(Tangem_Card card) - { - return card.hasBalanceInfo(); - } - - public Uri getShareWalletURI(Tangem_Card mCard) - { - return Uri.parse("" + mCard.getWallet()); - } - - public Uri getShareWalletURIExplorer(Tangem_Card mCard) - { - if(mCard.getBlockchain() == Blockchain.EthereumTestNet) - return Uri.parse("https://rinkeby.etherscan.io/address/" + mCard.getWallet()); - else - return Uri.parse("https://etherscan.io/address/" + mCard.getWallet()); - } - - public boolean CheckUnspentTransaction(Tangem_Card mCard) - { - return true; - } - - - public boolean CheckAmountValie(Tangem_Card mCard, String amountValue, String feeValue, Long minFeeInInternalUnits) - { - Long fee = null; - Long amount = null; - try { - amount = mCard.InternalUnitsFromString(amountValue); - fee = mCard.InternalUnitsFromString(feeValue); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - - if(fee == null || amount == null) - return false; - - if(fee == 0 || amount ==0) - return false; - - - if(fee < minFeeInInternalUnits) - return false; - - - BigDecimal tmpFee = new BigDecimal(feeValue); - BigDecimal tmpAmount = new BigDecimal(amountValue); - tmpAmount = tmpAmount.multiply(new BigDecimal("1000000000")); - - if (tmpFee.compareTo(tmpAmount) > 0) - return false; - - return true; - } - - public String EvaluteFeeEquivalent(Tangem_Card mCard, String fee) - { - BigDecimal gweFee = new BigDecimal(fee); - gweFee = gweFee.divide(new BigDecimal("1000000000")); - gweFee = gweFee.setScale(18, RoundingMode.DOWN); - return GetAmountEqualentDescriptor(mCard, gweFee.toString()); - } - - public String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException { - Keccak256 kec = new Keccak256(); - int lenPk = pkUncompressed.length; - if (lenPk < 2) { - throw new IllegalArgumentException("Uncompress public key length is invald"); - } - byte[] cleanKey = new byte[lenPk - 1]; - for (int i = 0; i < cleanKey.length; ++i) { - cleanKey[i] = pkUncompressed[i + 1]; - } - byte[] r = kec.digest(cleanKey); - - byte[] address = new byte[20]; - for (int i = 0; i < 20; ++i) { - address[i] = r[i + 12]; - } - - return String.format("0x%s", BTCUtils.toHex(address)); - } - - public byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception { - - BigInteger nonceValue = mCard.GetConfirmTXCount(); - byte[] pbKey = mCard.getWalletPublicKey(); - boolean flag = (mCard.getSigningMethod()== Tangem_Card.SigningMethod.Sign_Hash_Validated_By_Issuer); - Issuer issuer = mCard.getIssuer(); - - - BigInteger fee = new BigInteger(feeValue, 10); - - BigDecimal amountDec = new BigDecimal(amountValue); - amountDec = amountDec.multiply(new BigDecimal("1000000000")); - - - BigInteger amount = amountDec.toBigInteger(); //new BigInteger(amountValue, 10); - amount = amount.subtract(fee); - - BigInteger nonce = nonceValue; - BigInteger gasPrice = fee.divide(BigInteger.valueOf(21000)); - BigInteger gasLimit = BigInteger.valueOf(21000); - Integer chainId = mCard.getBlockchain() == Blockchain.Ethereum ? ETH_Transaction.ChainEnum.Mainnet.getValue() : ETH_Transaction.ChainEnum.Rinkeby.getValue(); - - Long multiplicator = 1000000000L; - amount = amount.multiply(BigInteger.valueOf(multiplicator)); - gasPrice = gasPrice.multiply(BigInteger.valueOf(multiplicator)); - - String to = toValue; - - if (to.startsWith("0x") || to.startsWith("0X")) { - to = to.substring(2); - } - - ETH_Transaction tx = ETH_Transaction.create(to, amount, nonce, gasPrice, gasLimit, chainId); - - byte[][] hashesForSign = new byte[1][]; - byte[] for_hash = tx.getRawHash(); - hashesForSign[0] = for_hash; - - byte[] signFromCard = null; - try { - signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, flag, null, issuer).getTLV(TLV.Tag.TAG_Signature).Value; - // TODO slice signFromCard to hashes.length parts - } catch (Exception ex) { - Log.e("ETH", ex.getMessage()); - return null; - } - - LastSignStorage.setLastSignDate(mCard.getWallet(), new Date()); - - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); - s = CryptoUtil.toCanonicalised(s); - - boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey); - - if(!f) - { - Log.e("ETH-CHECK", "Sign Failed."); - } - - tx.signature = new ECDSASignature_ETH(r, s); - int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey); - if (v != 27 && v != 28) { - Log.e("ETH", "invalid v"); - return null; - } - tx.signature.v = (byte) v; - Log.e("ETH_v", String.valueOf(v)); - - byte[] realTX = tx.getEncoded(); - return realTX; - } -} +package com.tangem.wallet; + +import android.net.Uri; +import android.util.Log; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.TLV; + +import org.bitcoinj.core.ECKey; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.Date; + +import static com.tangem.wallet.FormatUtil.GetDecimalFormat; + +/** + * Created by Ilia on 15.02.2018. + */ + +public class EthEngine extends CoinEngine{ + + public String GetNextNode(Tangem_Card mCard) + { + return "abc1.hsmiths.com"; + } + + public int GetNextNodePort(Tangem_Card mCard) + { + return 60001; + } + + public String GetNode(Tangem_Card mCard) + { + return "abc1.hsmiths.com"; + } + + public int GetNodePort(Tangem_Card mCard) + { + return 60001; + } + + public void SwitchNode(Tangem_Card mCard) + { + } + + public boolean InOutPutVisible() + { + return false; + } + + public String GetBalanceCurrency(Tangem_Card card) + { + return "ETH"; + } + + public boolean AwaitingConfirmation(Tangem_Card card) + { + return false; + } + + public String GetFeeCurrency() + { + return "Gwei"; + } + + public boolean IsNeedCheckNode() + { + return false; + } + + BigDecimal convertToEth(String value) + { + BigInteger m = new BigInteger(value, 10); + BigDecimal n = new BigDecimal(m); + BigDecimal d = n.divide(new BigDecimal("1000000000000000000")); + d = d.setScale(8, RoundingMode.DOWN); + return d; + } + + public int GetTokenDecimals(Tangem_Card card) + { + return 0; + } + + public String GetContractAddress(Tangem_Card card) + { + return ""; + } + + public boolean ValdateAddress(String address, Tangem_Card card) { + + if (address == null || address.isEmpty()) + { + return false; + } + + if(!address.startsWith("0x")&&!address.startsWith("0X")) + { + return false; + } + + if(address.length()!=42) + { + return false; + } + + return true; + } + public String GetBalanceValue(Tangem_Card mCard) + { + String dec = mCard.getDecimalBalance(); + BigDecimal d = convertToEth(dec); + String s = d.toString(); + + String pattern = "#0.000"; // If you like 4 zeros + DecimalFormat myFormatter = new DecimalFormat(pattern); + String output = myFormatter.format(d); + return output; + } + + public static String getAmountEquivalentDescriptionETH(BigDecimal amount, float rateValue) { + if (amount == null || amount.compareTo(BigDecimal.ZERO) == 0) + return ""; + + if (rateValue > 0) { + BigDecimal biRate = new BigDecimal(rateValue); + BigDecimal exchangeCurs = biRate.multiply(amount); + exchangeCurs = exchangeCurs.setScale(2, RoundingMode.DOWN); + return "≈ USD  " + exchangeCurs.toString(); + } else { + return "≈ USD  ---"; + } + } + + public static String getAmountEquivalentDescriptionETH(Double amount, float rate) { + if (amount == 0) + return ""; + amount = amount / 100000; + if (rate > 0) { + return String.format("≈ USD %.2f", amount * rate); + } else { + return "≈ USD  ---"; + } + } + + + + @Override + public String GetBalanceEquivalent(Tangem_Card mCard) { + String dec = mCard.getDecimalBalance(); + BigDecimal d = convertToEth(dec); + return getAmountEquivalentDescriptionETH(d, mCard.getRate()); + } + + @Override + public String GetBalance(Tangem_Card mCard) { + if(!HasBalanceInfo(mCard)){ + return "-- -- -- " + GetBalanceCurrency(mCard); + } + + String output = GetBalanceValue(mCard); + String s = output + " " + GetBalanceCurrency(mCard); + return s; + } + + public Long GetBalanceLong(Tangem_Card mCard) + { + return mCard.getBalance(); + } + + public String GetBalanceWithAlter(Tangem_Card mCard) + { + return GetBalance(mCard); + } + + public boolean IsBalanceAlterNotZero(Tangem_Card card) + { + return true; + } + + public boolean IsBalanceNotZero(Tangem_Card card) + { + String balance = card.getDecimalBalance(); + if(balance == null || balance == "") + return false; + + BigDecimal bi = new BigDecimal(balance); + + if (BigDecimal.ZERO.compareTo(bi) == 0) + return false; + + return true; + } + + @Override + public String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public byte[] ConvertAmountToByteArray(Tangem_Card mCard, String amount) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public String GetAmountDescription(Tangem_Card mCard, String amount) throws Exception { + throw new Exception("Not implemented"); + } + + public String GetAmountEqualentDescriptor(Tangem_Card mCard, String value) + { + BigDecimal d = new BigDecimal(value); + return getAmountEquivalentDescriptionETH(d, mCard.getRate()); + } + + public boolean CheckAmount(Tangem_Card card, String amount) throws Exception + { + DecimalFormat decimalFormat = GetDecimalFormat(); + BigDecimal amountValue = (BigDecimal) decimalFormat.parse(amount); //new BigDecimal(strAmount); + BigDecimal maxValue = new BigDecimal(GetBalanceValue(card)); + if(amountValue.compareTo(maxValue) > 0 ) + { + return false; + } + + return true; + } + + public boolean HasBalanceInfo(Tangem_Card card) + { + return card.hasBalanceInfo(); + } + + public Uri getShareWalletURI(Tangem_Card mCard) + { + return Uri.parse("" + mCard.getWallet()); + } + + public Uri getShareWalletURIExplorer(Tangem_Card mCard) + { + if(mCard.getBlockchain() == Blockchain.EthereumTestNet) + return Uri.parse("https://rinkeby.etherscan.io/address/" + mCard.getWallet()); + else + return Uri.parse("https://etherscan.io/address/" + mCard.getWallet()); + } + + public boolean CheckUnspentTransaction(Tangem_Card mCard) + { + return true; + } + + + public boolean CheckAmountValie(Tangem_Card mCard, String amountValue, String feeValue, Long minFeeInInternalUnits) + { + Long fee = null; + Long amount = null; + try { + amount = mCard.InternalUnitsFromString(amountValue); + fee = mCard.InternalUnitsFromString(feeValue); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + if(fee == null || amount == null) + return false; + + if(fee == 0 || amount ==0) + return false; + + + if(fee < minFeeInInternalUnits) + return false; + + + BigDecimal tmpFee = new BigDecimal(feeValue); + BigDecimal tmpAmount = new BigDecimal(amountValue); + tmpAmount = tmpAmount.multiply(new BigDecimal("1000000000")); + + if (tmpFee.compareTo(tmpAmount) > 0) + return false; + + return true; + } + + public String EvaluteFeeEquivalent(Tangem_Card mCard, String fee) + { + BigDecimal gweFee = new BigDecimal(fee); + gweFee = gweFee.divide(new BigDecimal("1000000000")); + gweFee = gweFee.setScale(18, RoundingMode.DOWN); + return GetAmountEqualentDescriptor(mCard, gweFee.toString()); + } + + public String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException { + Keccak256 kec = new Keccak256(); + int lenPk = pkUncompressed.length; + if (lenPk < 2) { + throw new IllegalArgumentException("Uncompress public key length is invald"); + } + byte[] cleanKey = new byte[lenPk - 1]; + for (int i = 0; i < cleanKey.length; ++i) { + cleanKey[i] = pkUncompressed[i + 1]; + } + byte[] r = kec.digest(cleanKey); + + byte[] address = new byte[20]; + for (int i = 0; i < 20; ++i) { + address[i] = r[i + 12]; + } + + return String.format("0x%s", BTCUtils.toHex(address)); + } + + public byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception { + + BigInteger nonceValue = mCard.GetConfirmTXCount(); + byte[] pbKey = mCard.getWalletPublicKey(); + boolean flag = (mCard.getSigningMethod()== Tangem_Card.SigningMethod.Sign_Hash_Validated_By_Issuer); + Issuer issuer = mCard.getIssuer(); + + + BigInteger fee = new BigInteger(feeValue, 10); + + BigDecimal amountDec = new BigDecimal(amountValue); + amountDec = amountDec.multiply(new BigDecimal("1000000000")); + + + BigInteger amount = amountDec.toBigInteger(); //new BigInteger(amountValue, 10); + amount = amount.subtract(fee); + + BigInteger nonce = nonceValue; + BigInteger gasPrice = fee.divide(BigInteger.valueOf(21000)); + BigInteger gasLimit = BigInteger.valueOf(21000); + Integer chainId = mCard.getBlockchain() == Blockchain.Ethereum ? ETH_Transaction.ChainEnum.Mainnet.getValue() : ETH_Transaction.ChainEnum.Rinkeby.getValue(); + + Long multiplicator = 1000000000L; + amount = amount.multiply(BigInteger.valueOf(multiplicator)); + gasPrice = gasPrice.multiply(BigInteger.valueOf(multiplicator)); + + String to = toValue; + + if (to.startsWith("0x") || to.startsWith("0X")) { + to = to.substring(2); + } + + ETH_Transaction tx = ETH_Transaction.create(to, amount, nonce, gasPrice, gasLimit, chainId); + + byte[][] hashesForSign = new byte[1][]; + byte[] for_hash = tx.getRawHash(); + hashesForSign[0] = for_hash; + + byte[] signFromCard = null; + try { + signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, flag, null, issuer).getTLV(TLV.Tag.TAG_Signature).Value; + // TODO slice signFromCard to hashes.length parts + } catch (Exception ex) { + Log.e("ETH", ex.getMessage()); + return null; + } + + LastSignStorage.setLastSignDate(mCard.getWallet(), new Date()); + + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); + s = CryptoUtil.toCanonicalised(s); + + boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey); + + if(!f) + { + Log.e("ETH-CHECK", "Sign Failed."); + } + + tx.signature = new ECDSASignature_ETH(r, s); + int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey); + if (v != 27 && v != 28) { + Log.e("ETH", "invalid v"); + return null; + } + tx.signature.v = (byte) v; + Log.e("ETH_v", String.valueOf(v)); + + byte[] realTX = tx.getEncoded(); + return realTX; + } +} diff --git a/app/src/main/java/com/tangem/wallet/ExchangeRequest.java b/app/src/main/java/com/tangem/wallet/ExchangeRequest.java index 942cdbe112..8d48b16175 100644 --- a/app/src/main/java/com/tangem/wallet/ExchangeRequest.java +++ b/app/src/main/java/com/tangem/wallet/ExchangeRequest.java @@ -1,92 +1,92 @@ -package com.tangem.wallet; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Created by dvol on 16.07.2017. - */ - -public class ExchangeRequest { - public JSONObject jsRequestData; - public String answerData; - public String error; - public String WalletAddress; - public String currency; - public String currencyAlter; - - private ExchangeRequest() { - } - - public ExchangeRequest(JSONObject jsRequest) { - try { - jsRequestData = new JSONObject(jsRequest.toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public JSONObject getAnswer() { - try { - return new JSONObject(answerData); - } catch (Exception e) { - try { - return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage())); - } catch (JSONException e1) { - e1.printStackTrace(); - return null; - } - } - } - - public JSONArray getAnswerList() throws JSONException { - return new JSONArray(answerData); - } - - public String getAsString() { - return jsRequestData.toString(); - } - - public void setID(int value) { - try { - jsRequestData.put("id", String.format("%d", value)); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public int getID() { - try { - return jsRequestData.getInt("id"); - } catch (JSONException e) { - e.printStackTrace(); - return 0; - } - } - - public static ExchangeRequest GetRate(String wallet, String currency, String alterCurrency) { - ExchangeRequest request = new ExchangeRequest(); - request.WalletAddress=wallet; - request.currency = currency; - request.currencyAlter = alterCurrency; - return request; - } - - - public JSONArray getParams() throws JSONException { - return jsRequestData.getJSONArray("params"); - } - - public JSONObject getResult() throws JSONException { - return getAnswer().getJSONObject("result"); - } - - public String getResultString() throws JSONException { - return getAnswer().getString("result"); - } - - public JSONArray getResultArray() throws JSONException { - return getAnswer().getJSONArray("result"); - } -} +package com.tangem.wallet; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Created by dvol on 16.07.2017. + */ + +public class ExchangeRequest { + public JSONObject jsRequestData; + public String answerData; + public String error; + public String WalletAddress; + public String currency; + public String currencyAlter; + + private ExchangeRequest() { + } + + public ExchangeRequest(JSONObject jsRequest) { + try { + jsRequestData = new JSONObject(jsRequest.toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public JSONObject getAnswer() { + try { + return new JSONObject(answerData); + } catch (Exception e) { + try { + return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage())); + } catch (JSONException e1) { + e1.printStackTrace(); + return null; + } + } + } + + public JSONArray getAnswerList() throws JSONException { + return new JSONArray(answerData); + } + + public String getAsString() { + return jsRequestData.toString(); + } + + public void setID(int value) { + try { + jsRequestData.put("id", String.format("%d", value)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public int getID() { + try { + return jsRequestData.getInt("id"); + } catch (JSONException e) { + e.printStackTrace(); + return 0; + } + } + + public static ExchangeRequest GetRate(String wallet, String currency, String alterCurrency) { + ExchangeRequest request = new ExchangeRequest(); + request.WalletAddress=wallet; + request.currency = currency; + request.currencyAlter = alterCurrency; + return request; + } + + + public JSONArray getParams() throws JSONException { + return jsRequestData.getJSONArray("params"); + } + + public JSONObject getResult() throws JSONException { + return getAnswer().getJSONObject("result"); + } + + public String getResultString() throws JSONException { + return getAnswer().getString("result"); + } + + public JSONArray getResultArray() throws JSONException { + return getAnswer().getJSONArray("result"); + } +} diff --git a/app/src/main/java/com/tangem/wallet/ExchangeTask.java b/app/src/main/java/com/tangem/wallet/ExchangeTask.java index a47d149d96..7d1634071e 100644 --- a/app/src/main/java/com/tangem/wallet/ExchangeTask.java +++ b/app/src/main/java/com/tangem/wallet/ExchangeTask.java @@ -1,66 +1,66 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 16.01.2018. - */ - -import android.os.AsyncTask; - -import com.tangem.wallet.ExchangeRequest; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Ilia on 04.12.2017. - */ - -public class ExchangeTask extends AsyncTask> { - public ExchangeTask() - { - - } - protected List doInBackground(ExchangeRequest... requests) { - List result = new ArrayList<>(); - for (int i = 0; i < requests.length; i++) { - result.add(requests[i]); - } - - for (ExchangeRequest request: result) - { - HttpURLConnection httpcon = null; - - try { - - URL url = new URL("https://api.coinmarketcap.com/v1/ticker/?convert=USD&lmit=10"); - httpcon = (HttpURLConnection) url.openConnection(); - httpcon.setRequestMethod("GET"); - - httpcon.connect(); - - BufferedReader in = new BufferedReader( - new InputStreamReader(httpcon.getInputStream())); - String inputLine; - StringBuffer response = new StringBuffer(); - - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); - } - in.close(); - - request.answerData = response.toString(); - - } catch (Exception e) { - request.error = e.getMessage(); - } finally { - httpcon.disconnect(); - } - } - - return result; - } -} +package com.tangem.wallet; + +/** + * Created by Ilia on 16.01.2018. + */ + +import android.os.AsyncTask; + +import com.tangem.wallet.ExchangeRequest; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Ilia on 04.12.2017. + */ + +public class ExchangeTask extends AsyncTask> { + public ExchangeTask() + { + + } + protected List doInBackground(ExchangeRequest... requests) { + List result = new ArrayList<>(); + for (int i = 0; i < requests.length; i++) { + result.add(requests[i]); + } + + for (ExchangeRequest request: result) + { + HttpURLConnection httpcon = null; + + try { + + URL url = new URL("https://api.coinmarketcap.com/v1/ticker/?convert=USD&lmit=10"); + httpcon = (HttpURLConnection) url.openConnection(); + httpcon.setRequestMethod("GET"); + + httpcon.connect(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(httpcon.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + request.answerData = response.toString(); + + } catch (Exception e) { + request.error = e.getMessage(); + } finally { + httpcon.disconnect(); + } + } + + return result; + } +} diff --git a/app/src/main/java/com/tangem/wallet/Fee_Request.java b/app/src/main/java/com/tangem/wallet/Fee_Request.java index 103ed9fb39..8170e82ac4 100644 --- a/app/src/main/java/com/tangem/wallet/Fee_Request.java +++ b/app/src/main/java/com/tangem/wallet/Fee_Request.java @@ -1,102 +1,102 @@ -package com.tangem.wallet; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Created by dvol on 16.07.2017. - */ - -public class Fee_Request { - public JSONObject jsRequestData; - public String answerData; - public String error; - public String WalletAddress; - public long txSize = 0; - - private Fee_Request() { - } - - public Fee_Request(JSONObject jsRequest) { - try { - jsRequestData = new JSONObject(jsRequest.toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public JSONObject getAnswer() { - try { - return new JSONObject(answerData); - } catch (Exception e) { - try { - return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage())); - } catch (JSONException e1) { - e1.printStackTrace(); - return null; - } - } - } - - public String getAsString() { - return answerData; - } - - public static final int PRIORITY = 2; - public static final int NORMAL = 3; - public static final int MINIMAL = 6; - private int blockCount = NORMAL; - public void setBlockCount(int count - ) - { - blockCount = count; - } - - public int getBlockCount() - { - return blockCount; - } - - public void setID(int value) { - try { - jsRequestData.put("id", String.format("%d", value)); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public int getID() { - try { - return jsRequestData.getInt("id"); - } catch (JSONException e) { - e.printStackTrace(); - return 0; - } - } - - public static Fee_Request GetFee(String wallet, long txSize, int blockCount) { - Fee_Request request = new Fee_Request(); - request.WalletAddress=wallet; - request.txSize = txSize; - request.setBlockCount(blockCount); - return request; - } - - - public JSONArray getParams() throws JSONException { - return jsRequestData.getJSONArray("params"); - } - - public JSONObject getResult() throws JSONException { - return getAnswer().getJSONObject("result"); - } - - public String getResultString() throws JSONException { - return getAnswer().getString("result"); - } - - public JSONArray getResultArray() throws JSONException { - return getAnswer().getJSONArray("result"); - } -} +package com.tangem.wallet; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Created by dvol on 16.07.2017. + */ + +public class Fee_Request { + public JSONObject jsRequestData; + public String answerData; + public String error; + public String WalletAddress; + public long txSize = 0; + + private Fee_Request() { + } + + public Fee_Request(JSONObject jsRequest) { + try { + jsRequestData = new JSONObject(jsRequest.toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public JSONObject getAnswer() { + try { + return new JSONObject(answerData); + } catch (Exception e) { + try { + return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage())); + } catch (JSONException e1) { + e1.printStackTrace(); + return null; + } + } + } + + public String getAsString() { + return answerData; + } + + public static final int PRIORITY = 2; + public static final int NORMAL = 3; + public static final int MINIMAL = 6; + private int blockCount = NORMAL; + public void setBlockCount(int count + ) + { + blockCount = count; + } + + public int getBlockCount() + { + return blockCount; + } + + public void setID(int value) { + try { + jsRequestData.put("id", String.format("%d", value)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public int getID() { + try { + return jsRequestData.getInt("id"); + } catch (JSONException e) { + e.printStackTrace(); + return 0; + } + } + + public static Fee_Request GetFee(String wallet, long txSize, int blockCount) { + Fee_Request request = new Fee_Request(); + request.WalletAddress=wallet; + request.txSize = txSize; + request.setBlockCount(blockCount); + return request; + } + + + public JSONArray getParams() throws JSONException { + return jsRequestData.getJSONArray("params"); + } + + public JSONObject getResult() throws JSONException { + return getAnswer().getJSONObject("result"); + } + + public String getResultString() throws JSONException { + return getAnswer().getString("result"); + } + + public JSONArray getResultArray() throws JSONException { + return getAnswer().getJSONArray("result"); + } +} diff --git a/app/src/main/java/com/tangem/wallet/Fee_Task.java b/app/src/main/java/com/tangem/wallet/Fee_Task.java index a8c5c35a94..4e686b8105 100644 --- a/app/src/main/java/com/tangem/wallet/Fee_Task.java +++ b/app/src/main/java/com/tangem/wallet/Fee_Task.java @@ -1,63 +1,63 @@ -package com.tangem.wallet; - -import android.os.AsyncTask; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Ilia on 04.12.2017. - */ - -public class Fee_Task extends AsyncTask> { - - SharedData sharedCounter = null; - - public Fee_Task(SharedData sharedData) - { - sharedCounter = sharedData; - } - protected List doInBackground(Fee_Request... requests) { - List result = new ArrayList<>(); - for (int i = 0; i < requests.length; i++) { - result.add(requests[i]); - } - - for (Fee_Request request: result) - { - HttpURLConnection httpcon = null; - - try { - - URL url = new URL("https://estimatefee.com/n/"+String.valueOf(request.getBlockCount())); - httpcon = (HttpURLConnection) url.openConnection(); - httpcon.setRequestMethod("GET"); - - httpcon.connect(); - - BufferedReader in = new BufferedReader( - new InputStreamReader(httpcon.getInputStream())); - String inputLine; - StringBuffer response = new StringBuffer(); - - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); - } - in.close(); - - request.answerData = response.toString(); - - } catch (Exception e) { - request.error = e.getMessage(); - } finally { - httpcon.disconnect(); - } - } - - return result; - } +package com.tangem.wallet; + +import android.os.AsyncTask; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Ilia on 04.12.2017. + */ + +public class Fee_Task extends AsyncTask> { + + SharedData sharedCounter = null; + + public Fee_Task(SharedData sharedData) + { + sharedCounter = sharedData; + } + protected List doInBackground(Fee_Request... requests) { + List result = new ArrayList<>(); + for (int i = 0; i < requests.length; i++) { + result.add(requests[i]); + } + + for (Fee_Request request: result) + { + HttpURLConnection httpcon = null; + + try { + + URL url = new URL("https://estimatefee.com/n/"+String.valueOf(request.getBlockCount())); + httpcon = (HttpURLConnection) url.openConnection(); + httpcon.setRequestMethod("GET"); + + httpcon.connect(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(httpcon.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + request.answerData = response.toString(); + + } catch (Exception e) { + request.error = e.getMessage(); + } finally { + httpcon.disconnect(); + } + } + + return result; + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/FormatUtil.java b/app/src/main/java/com/tangem/wallet/FormatUtil.java index 4a21adb789..b11db9a2a8 100644 --- a/app/src/main/java/com/tangem/wallet/FormatUtil.java +++ b/app/src/main/java/com/tangem/wallet/FormatUtil.java @@ -1,51 +1,51 @@ -package com.tangem.wallet; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.util.Locale; -import java.util.regex.Pattern; - -/** - * Created by Ilia on 15.02.2018. - */ - -public class FormatUtil { - public static long parseValue(String valueStr) throws NumberFormatException { - return new BigDecimal(valueStr).multiply(BigDecimal.valueOf(1_0000_0000)).setScale(0, BigDecimal.ROUND_HALF_DOWN).longValueExact(); - } - - public static String DoubleToString(double amount) - { - DecimalFormat myFormatter = GetDecimalFormat(); - String output = myFormatter.format(amount); - return output; - } - - public static DecimalFormat GetDecimalFormat() - { - DecimalFormatSymbols symbols = new DecimalFormatSymbols(); - symbols.setDecimalSeparator('.'); - - String pattern = "#0.######"; - DecimalFormat myFormatter = new DecimalFormat(pattern, symbols); - - myFormatter.setParseBigDecimal(true); - - return myFormatter; - } - - - public static long ConvertStringToLong(String caption) throws Exception { - - - BigDecimal d = new BigDecimal(caption); - d = d.multiply(new BigDecimal(100000)); - d = d.setScale(5); - BigInteger b = d.toBigInteger(); - long l = b.longValue(); - return l; - - } -} +package com.tangem.wallet; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; +import java.util.regex.Pattern; + +/** + * Created by Ilia on 15.02.2018. + */ + +public class FormatUtil { + public static long parseValue(String valueStr) throws NumberFormatException { + return new BigDecimal(valueStr).multiply(BigDecimal.valueOf(1_0000_0000)).setScale(0, BigDecimal.ROUND_HALF_DOWN).longValueExact(); + } + + public static String DoubleToString(double amount) + { + DecimalFormat myFormatter = GetDecimalFormat(); + String output = myFormatter.format(amount); + return output; + } + + public static DecimalFormat GetDecimalFormat() + { + DecimalFormatSymbols symbols = new DecimalFormatSymbols(); + symbols.setDecimalSeparator('.'); + + String pattern = "#0.######"; + DecimalFormat myFormatter = new DecimalFormat(pattern, symbols); + + myFormatter.setParseBigDecimal(true); + + return myFormatter; + } + + + public static long ConvertStringToLong(String caption) throws Exception { + + + BigDecimal d = new BigDecimal(caption); + d = d.multiply(new BigDecimal(100000)); + d = d.setScale(5); + BigInteger b = d.toBigInteger(); + long l = b.longValue(); + return l; + + } +} diff --git a/app/src/main/java/com/tangem/wallet/Infura_Request.java b/app/src/main/java/com/tangem/wallet/Infura_Request.java index 1f3465b6e9..1828cdcf35 100644 --- a/app/src/main/java/com/tangem/wallet/Infura_Request.java +++ b/app/src/main/java/com/tangem/wallet/Infura_Request.java @@ -1,197 +1,197 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 19.12.2017. - */ - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - - -public class Infura_Request { - public static final String METHOD_ETH_GetBalance = "eth_getBalance"; - public static final String METHOD_ETH_GetOutTransactionCount = "eth_getTransactionCount"; - public static final String METHOD_ETH_GetGasPrice = "eth_gasPrice"; - public static final String METHOD_ETH_SendRawTransaction = "eth_sendRawTransaction"; - public static final String METHOD_ETH_Call = "eth_call"; - - public JSONObject jsRequestData; - public String answerData; - public String error; - public String WalletAddress; - public int Dec; - public String amount; - public Blockchain blockchain; - - public void setBlockchain(Blockchain value) { - blockchain = value; - } - - public Blockchain getBlockchain() - { - return blockchain; - } - - private Infura_Request() { - } - - public Infura_Request(JSONObject jsRequest) { - try { - jsRequestData = new JSONObject(jsRequest.toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public JSONObject getAnswer() { - try { - return new JSONObject(answerData); - } catch (Exception e) { - try { - return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage())); - } catch (JSONException e1) { - e1.printStackTrace(); - return null; - } - } - } - - public String getAsString() { - return jsRequestData.toString(); - } - - public void setID(int value) { - try { - jsRequestData.put("id", value/*String.format("%d", value)*/); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public int getID() { - try { - return jsRequestData.getInt("id"); - } catch (JSONException e) { - e.printStackTrace(); - return 0; - } - } - - public boolean isMethod(String methodName) throws JSONException { - return jsRequestData.getString("method").equals(methodName); - } - - public static Infura_Request GetBalance(String wallet) { - Infura_Request request = new Infura_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_GetBalance + "\", \"params\":[\"" + wallet + "\", \"latest\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Infura_Request GetTokenBalance(String wallet, String contract, int dec) - { - Infura_Request request = new Infura_Request(); - try { - request.WalletAddress=wallet; - request.Dec = dec; - String address = wallet.substring(2); - String dataValue = String.format("{\"data\": \"0x70a08231000000000000000000000000%s\", \"to\": \"%s\"}", address, contract); - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_Call + "\", \"params\":[" +dataValue+", \"latest\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Infura_Request SendTransaction(String wallet, String tx) { - Infura_Request request = new Infura_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_SendRawTransaction + "\", \"params\":[\"" + tx + "\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - - - public static Infura_Request GetOutTransactionCount(String wallet) { - Infura_Request request = new Infura_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_GetOutTransactionCount + "\", \"params\":[\"" + wallet + "\", \"latest\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Infura_Request GetPendingTransactionCount(String wallet) { - Infura_Request request = new Infura_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_GetOutTransactionCount + "\", \"params\":[\"" + wallet + "\", \"pending\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - public static Infura_Request GetGasPrise(String wallet) { - Infura_Request request = new Infura_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_GetGasPrice + "\", \"params\":[] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - - public static Infura_Request SendTransactionCount(String wallet, String TX) { - Infura_Request request = new Infura_Request(); - try { - request.WalletAddress=wallet; - request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_SendRawTransaction + "\", \"params\":[\"" + TX + "\"] }"); - } catch (JSONException e) { - e.printStackTrace(); - request.error = e.toString(); - } - return request; - } - - - - //METHOD_ETH_GetOutTransactionCount - - - public JSONArray getParams() throws JSONException { - return jsRequestData.getJSONArray("params"); - } - - public JSONObject getResult() throws JSONException { - return getAnswer().getJSONObject("result"); - } - - public String getResultString() throws JSONException { - return getAnswer().getString("result"); - } - - public JSONArray getResultArray() throws JSONException { - return getAnswer().getJSONArray("result"); - } -} - +package com.tangem.wallet; + +/** + * Created by Ilia on 19.12.2017. + */ + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + + +public class Infura_Request { + public static final String METHOD_ETH_GetBalance = "eth_getBalance"; + public static final String METHOD_ETH_GetOutTransactionCount = "eth_getTransactionCount"; + public static final String METHOD_ETH_GetGasPrice = "eth_gasPrice"; + public static final String METHOD_ETH_SendRawTransaction = "eth_sendRawTransaction"; + public static final String METHOD_ETH_Call = "eth_call"; + + public JSONObject jsRequestData; + public String answerData; + public String error; + public String WalletAddress; + public int Dec; + public String amount; + public Blockchain blockchain; + + public void setBlockchain(Blockchain value) { + blockchain = value; + } + + public Blockchain getBlockchain() + { + return blockchain; + } + + private Infura_Request() { + } + + public Infura_Request(JSONObject jsRequest) { + try { + jsRequestData = new JSONObject(jsRequest.toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public JSONObject getAnswer() { + try { + return new JSONObject(answerData); + } catch (Exception e) { + try { + return new JSONObject(String.format("[\"Error\":\"%s\"]", e.getMessage())); + } catch (JSONException e1) { + e1.printStackTrace(); + return null; + } + } + } + + public String getAsString() { + return jsRequestData.toString(); + } + + public void setID(int value) { + try { + jsRequestData.put("id", value/*String.format("%d", value)*/); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public int getID() { + try { + return jsRequestData.getInt("id"); + } catch (JSONException e) { + e.printStackTrace(); + return 0; + } + } + + public boolean isMethod(String methodName) throws JSONException { + return jsRequestData.getString("method").equals(methodName); + } + + public static Infura_Request GetBalance(String wallet) { + Infura_Request request = new Infura_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_GetBalance + "\", \"params\":[\"" + wallet + "\", \"latest\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Infura_Request GetTokenBalance(String wallet, String contract, int dec) + { + Infura_Request request = new Infura_Request(); + try { + request.WalletAddress=wallet; + request.Dec = dec; + String address = wallet.substring(2); + String dataValue = String.format("{\"data\": \"0x70a08231000000000000000000000000%s\", \"to\": \"%s\"}", address, contract); + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_Call + "\", \"params\":[" +dataValue+", \"latest\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Infura_Request SendTransaction(String wallet, String tx) { + Infura_Request request = new Infura_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_SendRawTransaction + "\", \"params\":[\"" + tx + "\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + + + public static Infura_Request GetOutTransactionCount(String wallet) { + Infura_Request request = new Infura_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_GetOutTransactionCount + "\", \"params\":[\"" + wallet + "\", \"latest\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Infura_Request GetPendingTransactionCount(String wallet) { + Infura_Request request = new Infura_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_GetOutTransactionCount + "\", \"params\":[\"" + wallet + "\", \"pending\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + public static Infura_Request GetGasPrise(String wallet) { + Infura_Request request = new Infura_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_GetGasPrice + "\", \"params\":[] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + + public static Infura_Request SendTransactionCount(String wallet, String TX) { + Infura_Request request = new Infura_Request(); + try { + request.WalletAddress=wallet; + request.jsRequestData = new JSONObject("{ \"method\":\"" + METHOD_ETH_SendRawTransaction + "\", \"params\":[\"" + TX + "\"] }"); + } catch (JSONException e) { + e.printStackTrace(); + request.error = e.toString(); + } + return request; + } + + + + //METHOD_ETH_GetOutTransactionCount + + + public JSONArray getParams() throws JSONException { + return jsRequestData.getJSONArray("params"); + } + + public JSONObject getResult() throws JSONException { + return getAnswer().getJSONObject("result"); + } + + public String getResultString() throws JSONException { + return getAnswer().getString("result"); + } + + public JSONArray getResultArray() throws JSONException { + return getAnswer().getJSONArray("result"); + } +} + diff --git a/app/src/main/java/com/tangem/wallet/Infura_Task.java b/app/src/main/java/com/tangem/wallet/Infura_Task.java index 7e0ded0162..18d9b2d4b6 100644 --- a/app/src/main/java/com/tangem/wallet/Infura_Task.java +++ b/app/src/main/java/com/tangem/wallet/Infura_Task.java @@ -1,134 +1,134 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 19.12.2017. - */ - -import android.os.AsyncTask; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -import javax.net.ssl.HttpsURLConnection; - -/** - * Created by Ilia on 04.12.2017. - */ - -public class Infura_Task extends AsyncTask> { - private Exception exception; - private Blockchain blockchain; - public Infura_Task(Blockchain blockchainNet) - { - blockchain = blockchainNet; - } - boolean useOurNode = false; - protected List doInBackground(Infura_Request... requests) { - List result = new ArrayList<>(); - for (int i = 0; i < requests.length; i++) { - result.add(requests[i]); - } - - for (Infura_Request request: result) - { - HttpURLConnection httpcon = null; - - try { - URL url = new URL("https://rinkeby.infura.io/AfWg0tmYEX5Kukn2UkKV"); - - if(blockchain == Blockchain.Ethereum || blockchain == Blockchain.Token){ - if(useOurNode) { - URL tmp = new URL("http://52.230.23.88"); - url = new URL(tmp.getProtocol(), tmp.getHost(), 27172, tmp.getFile()); - }else - url = new URL("https://mainnet.infura.io/AfWg0tmYEX5Kukn2UkKV"); - - } - - if(useOurNode) - { - httpcon = (HttpURLConnection)url.openConnection(); - } - else - { - httpcon = (HttpsURLConnection)url.openConnection(); - } - - if(httpcon == null) - { - request.error = String.format("Cann't connect to %s", url.getHost()); - return result; - } - - httpcon.setRequestMethod("POST"); - httpcon.setRequestProperty("Content-Type", "application/json"); - String params = request.getAsString(); - - OutputStream os = httpcon.getOutputStream(); - if(os == null) - { - request.error = String.format("Cann't recieve data from %s", url.getHost()); - return result; - } - BufferedWriter writer = new BufferedWriter( - new OutputStreamWriter(os, "UTF-8")); - if(writer == null) - { - request.error = String.format("Cann't send data to %s", url.getHost()); - - } - writer.write(params); - writer.flush(); - writer.close(); - os.close(); - - - httpcon.connect(); - request.getParams(); - System.out.println("code:"+httpcon.getResponseCode()); - int code = httpcon.getResponseCode(); - - BufferedReader in = new BufferedReader( - new InputStreamReader(httpcon.getInputStream())); - String inputLine; - StringBuffer response = new StringBuffer(); - - while ((inputLine = in.readLine()) != null) { - response.append(inputLine); - } - in.close(); - - request.answerData = response.toString(); - - } catch (Exception e) { - this.exception = e; - request.error = e.getMessage(); - } finally { - httpcon.disconnect(); - } - } - - return result; - } - - public String getValidationNodeDescription() { - if(blockchain == Blockchain.Ethereum || blockchain == Blockchain.Token) - { - if(useOurNode) - return "52.230.23.88:27172"; - else - return "Infura, infura.io"; - } - - - return "Infura, rinkeby.infura.io"; - } - -} +package com.tangem.wallet; + +/** + * Created by Ilia on 19.12.2017. + */ + +import android.os.AsyncTask; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import javax.net.ssl.HttpsURLConnection; + +/** + * Created by Ilia on 04.12.2017. + */ + +public class Infura_Task extends AsyncTask> { + private Exception exception; + private Blockchain blockchain; + public Infura_Task(Blockchain blockchainNet) + { + blockchain = blockchainNet; + } + boolean useOurNode = false; + protected List doInBackground(Infura_Request... requests) { + List result = new ArrayList<>(); + for (int i = 0; i < requests.length; i++) { + result.add(requests[i]); + } + + for (Infura_Request request: result) + { + HttpURLConnection httpcon = null; + + try { + URL url = new URL("https://rinkeby.infura.io/AfWg0tmYEX5Kukn2UkKV"); + + if(blockchain == Blockchain.Ethereum || blockchain == Blockchain.Token){ + if(useOurNode) { + URL tmp = new URL("http://52.230.23.88"); + url = new URL(tmp.getProtocol(), tmp.getHost(), 27172, tmp.getFile()); + }else + url = new URL("https://mainnet.infura.io/AfWg0tmYEX5Kukn2UkKV"); + + } + + if(useOurNode) + { + httpcon = (HttpURLConnection)url.openConnection(); + } + else + { + httpcon = (HttpsURLConnection)url.openConnection(); + } + + if(httpcon == null) + { + request.error = String.format("Cann't connect to %s", url.getHost()); + return result; + } + + httpcon.setRequestMethod("POST"); + httpcon.setRequestProperty("Content-Type", "application/json"); + String params = request.getAsString(); + + OutputStream os = httpcon.getOutputStream(); + if(os == null) + { + request.error = String.format("Cann't recieve data from %s", url.getHost()); + return result; + } + BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(os, "UTF-8")); + if(writer == null) + { + request.error = String.format("Cann't send data to %s", url.getHost()); + + } + writer.write(params); + writer.flush(); + writer.close(); + os.close(); + + + httpcon.connect(); + request.getParams(); + System.out.println("code:"+httpcon.getResponseCode()); + int code = httpcon.getResponseCode(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(httpcon.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + request.answerData = response.toString(); + + } catch (Exception e) { + this.exception = e; + request.error = e.getMessage(); + } finally { + httpcon.disconnect(); + } + } + + return result; + } + + public String getValidationNodeDescription() { + if(blockchain == Blockchain.Ethereum || blockchain == Blockchain.Token) + { + if(useOurNode) + return "52.230.23.88:27172"; + else + return "Infura, infura.io"; + } + + + return "Infura, rinkeby.infura.io"; + } + +} diff --git a/app/src/main/java/com/tangem/wallet/Issuer.java b/app/src/main/java/com/tangem/wallet/Issuer.java index 2c1a50ee73..39d55271e5 100644 --- a/app/src/main/java/com/tangem/wallet/Issuer.java +++ b/app/src/main/java/com/tangem/wallet/Issuer.java @@ -1,126 +1,126 @@ -package com.tangem.wallet; - -import com.tangem.cardReader.CardCrypto; - -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; - -import java.math.BigInteger; -import java.util.Arrays; - -import static com.tangem.cardReader.CardCrypto.*; - -/** - * Created by dvol on 14.11.2017. - */ - -public enum Issuer { - Unknown("Unknown", "Unknown", null, null, null, null), - SMART_CASH_AG("SMART CASH AG", "SMART CASH AG", - IssuerKeyStorage.sdkPrivateDataKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateDataKey), - IssuerKeyStorage.sdkPrivateTransactionKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateTransactionKey)), - TANGEM_SDK("TANGEM SDK", "TANGEM SDK", - IssuerKeyStorage.sdkPrivateDataKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateDataKey), - IssuerKeyStorage.sdkPrivateTransactionKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateTransactionKey)), - TANGEM("TANGEM", "TANGEM", null, IssuerKeyStorage.tangemPublicDataKey, null, IssuerKeyStorage.tangemPublicTransactionKey) - ; - - - static class IssuerKeyStorage { - private static final byte[] sdkPrivateDataKey = new byte[]{ - (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, - (byte) 0x47, (byte) 0x71, (byte) 0xED, (byte) 0x81, (byte) 0xF2, (byte) 0xBA, (byte) 0xCF, (byte) 0x57, - (byte) 0x47, (byte) 0x9E, (byte) 0x47, (byte) 0x35, (byte) 0xEB, (byte) 0x14, (byte) 0x05, (byte) 0x08, - (byte) 0x39, (byte) 0x27, (byte) 0x37, (byte) 0x2D, (byte) 0x40, (byte) 0xDA, (byte) 0x9E, (byte) 0x92}; - - private static final byte[] sdkPrivateTransactionKey = new byte[]{ - (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, - (byte) 0x47, (byte) 0x71, (byte) 0xED, (byte) 0x81, (byte) 0xF2, (byte) 0xBA, (byte) 0xCF, (byte) 0x57, - (byte) 0x47, (byte) 0x9E, (byte) 0x47, (byte) 0x35, (byte) 0xEB, (byte) 0x14, (byte) 0x05, (byte) 0x08, - (byte) 0x19, (byte) 0x18, (byte) 0x17, (byte) 0x16, (byte) 0x15, (byte) 0x14, (byte) 0x13, (byte) 0x12}; - - private static byte[] tangemPublicDataKey = { - (byte) 0x04 , - (byte) 0x81 ,(byte) 0x96 ,(byte) 0xAA ,(byte) 0x4B ,(byte) 0x41 ,(byte) 0x0A ,(byte) 0xC4 ,(byte) 0x4A, - (byte) 0x3B ,(byte) 0x9C ,(byte) 0xCE ,(byte) 0x18 ,(byte) 0xE7 ,(byte) 0xBE ,(byte) 0x22 ,(byte) 0x6A, - (byte) 0xEA ,(byte) 0x07 ,(byte) 0x0A ,(byte) 0xCC ,(byte) 0x83 ,(byte) 0xA9 ,(byte) 0xCF ,(byte) 0x67, - (byte) 0x54 ,(byte) 0x0F ,(byte) 0xAC ,(byte) 0x49 ,(byte) 0xAF ,(byte) 0x25 ,(byte) 0x12 ,(byte) 0x9F, - (byte) 0x6A ,(byte) 0x53 ,(byte) 0x8A ,(byte) 0x28 ,(byte) 0xAD ,(byte) 0x63 ,(byte) 0x41 ,(byte) 0x35, - (byte) 0x8E ,(byte) 0x3C ,(byte) 0x4F ,(byte) 0x99 ,(byte) 0x63 ,(byte) 0x06 ,(byte) 0x4F ,(byte) 0x7E, - (byte) 0x36 ,(byte) 0x53 ,(byte) 0x72 ,(byte) 0xA6 ,(byte) 0x51 ,(byte) 0xD3 ,(byte) 0x74 ,(byte) 0xE5, - (byte) 0xC2 ,(byte) 0x3C ,(byte) 0xDD ,(byte) 0x37 ,(byte) 0xFD ,(byte) 0x09 ,(byte) 0x9B ,(byte) 0xF2}; - - private static byte[] tangemPublicTransactionKey = { - (byte) 0x04 , - (byte) 0x34 ,(byte) 0x3D ,(byte) 0x40 ,(byte) 0x49 ,(byte) 0x6C ,(byte) 0xBE ,(byte) 0x1F ,(byte) 0xE8, - (byte) 0xA8 ,(byte) 0xC0 ,(byte) 0x26 ,(byte) 0x57 ,(byte) 0x5C ,(byte) 0x43 ,(byte) 0x5A ,(byte) 0x29, - (byte) 0x14 ,(byte) 0x1E ,(byte) 0xA3 ,(byte) 0xBC ,(byte) 0x33 ,(byte) 0x5D ,(byte) 0xA5 ,(byte) 0x54, - (byte) 0x9A ,(byte) 0xB6 ,(byte) 0xC6 ,(byte) 0x46 ,(byte) 0x85 ,(byte) 0xA6 ,(byte) 0x46 ,(byte) 0x84, - (byte) 0x80 ,(byte) 0x36 ,(byte) 0xD4 ,(byte) 0x81 ,(byte) 0xCF ,(byte) 0x9A ,(byte) 0x98 ,(byte) 0x93, - (byte) 0x90 ,(byte) 0xA8 ,(byte) 0xB0 ,(byte) 0x34 ,(byte) 0xB2 ,(byte) 0x29 ,(byte) 0xD9 ,(byte) 0x9B, - (byte) 0xD4 ,(byte) 0x9E ,(byte) 0x6F ,(byte) 0x07 ,(byte) 0xD2 ,(byte) 0xFF ,(byte) 0x02 ,(byte) 0x74, - (byte) 0x6E ,(byte) 0xA2 ,(byte) 0x65 ,(byte) 0xEF ,(byte) 0x99 ,(byte) 0x38 ,(byte) 0x0A ,(byte) 0x80}; - - public static byte[] GeneratePublicKey(byte[] privateKey) { - try { - return CardCrypto.GeneratePublicKey(privateKey); - } - catch (Exception e) - { - e.printStackTrace(); - return null; - } - } - } - - private String ID; - private String officialName; - private byte[] privateDataKeyArray; - private byte[] publicDataKeyArray; - private byte[] privateTransactionKeyArray; - private byte[] publicTransactionKeyArray; - - Issuer(String id, String officialName, byte[] privateDataKey, byte[] publicDataKey, byte[] privateTransactionKey, byte[] publicTransactionKey) { - this.ID = id; - this.officialName = officialName; - this.privateDataKeyArray = privateDataKey; - this.privateTransactionKeyArray = privateTransactionKey; - this.publicDataKeyArray = publicDataKey; - this.publicTransactionKeyArray = publicTransactionKey; - } - - public byte[] getPublicDataKey() { - return publicDataKeyArray; - } - - public byte[] getPublicTransactionKey() { - return publicTransactionKeyArray; - } - - public byte[] getPrivateDataKey() { - return privateDataKeyArray; - } - - public byte[] getPrivateTransactionKey() { - return privateTransactionKeyArray; - } - - public byte[] getID() { - return ID.getBytes(); - } - - public String getOfficialName() { - return officialName; - } - - public static Issuer FindIssuer(String ID, byte[] publicDataKey) { - Issuer[] issuers = Issuer.values(); - for (int i = 1; i < issuers.length; i++) { - if (issuers[i].ID.equals(ID) && Arrays.equals(issuers[i].getPublicDataKey(), publicDataKey)) { - return issuers[i]; - } - } - return Issuer.Unknown; - } - -} +package com.tangem.wallet; + +import com.tangem.cardReader.CardCrypto; + +import org.spongycastle.jce.ECNamedCurveTable; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; + +import java.math.BigInteger; +import java.util.Arrays; + +import static com.tangem.cardReader.CardCrypto.*; + +/** + * Created by dvol on 14.11.2017. + */ + +public enum Issuer { + Unknown("Unknown", "Unknown", null, null, null, null), + SMART_CASH_AG("SMART CASH AG", "SMART CASH AG", + IssuerKeyStorage.sdkPrivateDataKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateDataKey), + IssuerKeyStorage.sdkPrivateTransactionKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateTransactionKey)), + TANGEM_SDK("TANGEM SDK", "TANGEM SDK", + IssuerKeyStorage.sdkPrivateDataKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateDataKey), + IssuerKeyStorage.sdkPrivateTransactionKey, IssuerKeyStorage.GeneratePublicKey(IssuerKeyStorage.sdkPrivateTransactionKey)), + TANGEM("TANGEM", "TANGEM", null, IssuerKeyStorage.tangemPublicDataKey, null, IssuerKeyStorage.tangemPublicTransactionKey) + ; + + + static class IssuerKeyStorage { + private static final byte[] sdkPrivateDataKey = new byte[]{ + (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, + (byte) 0x47, (byte) 0x71, (byte) 0xED, (byte) 0x81, (byte) 0xF2, (byte) 0xBA, (byte) 0xCF, (byte) 0x57, + (byte) 0x47, (byte) 0x9E, (byte) 0x47, (byte) 0x35, (byte) 0xEB, (byte) 0x14, (byte) 0x05, (byte) 0x08, + (byte) 0x39, (byte) 0x27, (byte) 0x37, (byte) 0x2D, (byte) 0x40, (byte) 0xDA, (byte) 0x9E, (byte) 0x92}; + + private static final byte[] sdkPrivateTransactionKey = new byte[]{ + (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17, (byte) 0x18, + (byte) 0x47, (byte) 0x71, (byte) 0xED, (byte) 0x81, (byte) 0xF2, (byte) 0xBA, (byte) 0xCF, (byte) 0x57, + (byte) 0x47, (byte) 0x9E, (byte) 0x47, (byte) 0x35, (byte) 0xEB, (byte) 0x14, (byte) 0x05, (byte) 0x08, + (byte) 0x19, (byte) 0x18, (byte) 0x17, (byte) 0x16, (byte) 0x15, (byte) 0x14, (byte) 0x13, (byte) 0x12}; + + private static byte[] tangemPublicDataKey = { + (byte) 0x04 , + (byte) 0x81 ,(byte) 0x96 ,(byte) 0xAA ,(byte) 0x4B ,(byte) 0x41 ,(byte) 0x0A ,(byte) 0xC4 ,(byte) 0x4A, + (byte) 0x3B ,(byte) 0x9C ,(byte) 0xCE ,(byte) 0x18 ,(byte) 0xE7 ,(byte) 0xBE ,(byte) 0x22 ,(byte) 0x6A, + (byte) 0xEA ,(byte) 0x07 ,(byte) 0x0A ,(byte) 0xCC ,(byte) 0x83 ,(byte) 0xA9 ,(byte) 0xCF ,(byte) 0x67, + (byte) 0x54 ,(byte) 0x0F ,(byte) 0xAC ,(byte) 0x49 ,(byte) 0xAF ,(byte) 0x25 ,(byte) 0x12 ,(byte) 0x9F, + (byte) 0x6A ,(byte) 0x53 ,(byte) 0x8A ,(byte) 0x28 ,(byte) 0xAD ,(byte) 0x63 ,(byte) 0x41 ,(byte) 0x35, + (byte) 0x8E ,(byte) 0x3C ,(byte) 0x4F ,(byte) 0x99 ,(byte) 0x63 ,(byte) 0x06 ,(byte) 0x4F ,(byte) 0x7E, + (byte) 0x36 ,(byte) 0x53 ,(byte) 0x72 ,(byte) 0xA6 ,(byte) 0x51 ,(byte) 0xD3 ,(byte) 0x74 ,(byte) 0xE5, + (byte) 0xC2 ,(byte) 0x3C ,(byte) 0xDD ,(byte) 0x37 ,(byte) 0xFD ,(byte) 0x09 ,(byte) 0x9B ,(byte) 0xF2}; + + private static byte[] tangemPublicTransactionKey = { + (byte) 0x04 , + (byte) 0x34 ,(byte) 0x3D ,(byte) 0x40 ,(byte) 0x49 ,(byte) 0x6C ,(byte) 0xBE ,(byte) 0x1F ,(byte) 0xE8, + (byte) 0xA8 ,(byte) 0xC0 ,(byte) 0x26 ,(byte) 0x57 ,(byte) 0x5C ,(byte) 0x43 ,(byte) 0x5A ,(byte) 0x29, + (byte) 0x14 ,(byte) 0x1E ,(byte) 0xA3 ,(byte) 0xBC ,(byte) 0x33 ,(byte) 0x5D ,(byte) 0xA5 ,(byte) 0x54, + (byte) 0x9A ,(byte) 0xB6 ,(byte) 0xC6 ,(byte) 0x46 ,(byte) 0x85 ,(byte) 0xA6 ,(byte) 0x46 ,(byte) 0x84, + (byte) 0x80 ,(byte) 0x36 ,(byte) 0xD4 ,(byte) 0x81 ,(byte) 0xCF ,(byte) 0x9A ,(byte) 0x98 ,(byte) 0x93, + (byte) 0x90 ,(byte) 0xA8 ,(byte) 0xB0 ,(byte) 0x34 ,(byte) 0xB2 ,(byte) 0x29 ,(byte) 0xD9 ,(byte) 0x9B, + (byte) 0xD4 ,(byte) 0x9E ,(byte) 0x6F ,(byte) 0x07 ,(byte) 0xD2 ,(byte) 0xFF ,(byte) 0x02 ,(byte) 0x74, + (byte) 0x6E ,(byte) 0xA2 ,(byte) 0x65 ,(byte) 0xEF ,(byte) 0x99 ,(byte) 0x38 ,(byte) 0x0A ,(byte) 0x80}; + + public static byte[] GeneratePublicKey(byte[] privateKey) { + try { + return CardCrypto.GeneratePublicKey(privateKey); + } + catch (Exception e) + { + e.printStackTrace(); + return null; + } + } + } + + private String ID; + private String officialName; + private byte[] privateDataKeyArray; + private byte[] publicDataKeyArray; + private byte[] privateTransactionKeyArray; + private byte[] publicTransactionKeyArray; + + Issuer(String id, String officialName, byte[] privateDataKey, byte[] publicDataKey, byte[] privateTransactionKey, byte[] publicTransactionKey) { + this.ID = id; + this.officialName = officialName; + this.privateDataKeyArray = privateDataKey; + this.privateTransactionKeyArray = privateTransactionKey; + this.publicDataKeyArray = publicDataKey; + this.publicTransactionKeyArray = publicTransactionKey; + } + + public byte[] getPublicDataKey() { + return publicDataKeyArray; + } + + public byte[] getPublicTransactionKey() { + return publicTransactionKeyArray; + } + + public byte[] getPrivateDataKey() { + return privateDataKeyArray; + } + + public byte[] getPrivateTransactionKey() { + return privateTransactionKeyArray; + } + + public byte[] getID() { + return ID.getBytes(); + } + + public String getOfficialName() { + return officialName; + } + + public static Issuer FindIssuer(String ID, byte[] publicDataKey) { + Issuer[] issuers = Issuer.values(); + for (int i = 1; i < issuers.length; i++) { + if (issuers[i].ID.equals(ID) && Arrays.equals(issuers[i].getPublicDataKey(), publicDataKey)) { + return issuers[i]; + } + } + return Issuer.Unknown; + } + +} diff --git a/app/src/main/java/com/tangem/wallet/Keccak256.java b/app/src/main/java/com/tangem/wallet/Keccak256.java index bd8c2b5ded..b3162f63a8 100644 --- a/app/src/main/java/com/tangem/wallet/Keccak256.java +++ b/app/src/main/java/com/tangem/wallet/Keccak256.java @@ -1,39 +1,39 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 18.12.2017. - */ - -public class Keccak256 extends KeccakCore { - - /** - * Create the engine. - */ - public Keccak256() - { - super("eth-keccak-256"); - } - - public Digest copy() - { - return copyState(new Keccak256()); - } - - public int engineGetDigestLength() - { - return 32; - } - - @Override - protected byte[] engineDigest() { - return null; - } - - @Override - protected void engineUpdate(byte arg0) { - } - - @Override - protected void engineUpdate(byte[] arg0, int arg1, int arg2) { - } +package com.tangem.wallet; + +/** + * Created by Ilia on 18.12.2017. + */ + +public class Keccak256 extends KeccakCore { + + /** + * Create the engine. + */ + public Keccak256() + { + super("eth-keccak-256"); + } + + public Digest copy() + { + return copyState(new Keccak256()); + } + + public int engineGetDigestLength() + { + return 32; + } + + @Override + protected byte[] engineDigest() { + return null; + } + + @Override + protected void engineUpdate(byte arg0) { + } + + @Override + protected void engineUpdate(byte[] arg0, int arg1, int arg2) { + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/KeccakCore.java b/app/src/main/java/com/tangem/wallet/KeccakCore.java index 4eafa6de60..0f02ef6f79 100644 --- a/app/src/main/java/com/tangem/wallet/KeccakCore.java +++ b/app/src/main/java/com/tangem/wallet/KeccakCore.java @@ -1,546 +1,546 @@ -package com.tangem.wallet; - - -/** - * Created by Ilia on 18.12.2017. - */ -abstract class KeccakCore extends DigestEngine{ - KeccakCore(String alg) - { - super(alg); - } - - private long[] A; - private byte[] tmpOut; - - private static final long[] RC = { - 0x0000000000000001L, 0x0000000000008082L, - 0x800000000000808AL, 0x8000000080008000L, - 0x000000000000808BL, 0x0000000080000001L, - 0x8000000080008081L, 0x8000000000008009L, - 0x000000000000008AL, 0x0000000000000088L, - 0x0000000080008009L, 0x000000008000000AL, - 0x000000008000808BL, 0x800000000000008BL, - 0x8000000000008089L, 0x8000000000008003L, - 0x8000000000008002L, 0x8000000000000080L, - 0x000000000000800AL, 0x800000008000000AL, - 0x8000000080008081L, 0x8000000000008080L, - 0x0000000080000001L, 0x8000000080008008L - }; - - /** - * Encode the 64-bit word {@code val} into the array - * {@code buf} at offset {@code off}, in little-endian - * convention (least significant byte first). - * - * @param val the value to encode - * @param buf the destination buffer - * @param off the destination offset - */ - private static void encodeLELong(long val, byte[] buf, int off) - { - buf[off + 0] = (byte)val; - buf[off + 1] = (byte)(val >>> 8); - buf[off + 2] = (byte)(val >>> 16); - buf[off + 3] = (byte)(val >>> 24); - buf[off + 4] = (byte)(val >>> 32); - buf[off + 5] = (byte)(val >>> 40); - buf[off + 6] = (byte)(val >>> 48); - buf[off + 7] = (byte)(val >>> 56); - } - - /** - * Decode a 64-bit little-endian word from the array {@code buf} - * at offset {@code off}. - * - * @param buf the source buffer - * @param off the source offset - * @return the decoded value - */ - private static long decodeLELong(byte[] buf, int off) - { - return (buf[off + 0] & 0xFFL) - | ((buf[off + 1] & 0xFFL) << 8) - | ((buf[off + 2] & 0xFFL) << 16) - | ((buf[off + 3] & 0xFFL) << 24) - | ((buf[off + 4] & 0xFFL) << 32) - | ((buf[off + 5] & 0xFFL) << 40) - | ((buf[off + 6] & 0xFFL) << 48) - | ((buf[off + 7] & 0xFFL) << 56); - } - - protected void engineReset() - { - doReset(); - } - - protected void processBlock(byte[] data) - { - /* Input block */ - for (int i = 0; i < data.length; i += 8) - A[i >>> 3] ^= decodeLELong(data, i); - - long t0, t1, t2, t3, t4; - long tt0, tt1, tt2, tt3, tt4; - long t, kt; - long c0, c1, c2, c3, c4, bnn; - - /* - * Unrolling four rounds kills performance big time - * on Intel x86 Core2, in both 32-bit and 64-bit modes - * (less than 1 MB/s instead of 55 MB/s on x86-64). - * Unrolling two rounds appears to be fine. - */ - for (int j = 0; j < 24; j += 2) { - - tt0 = A[ 1] ^ A[ 6]; - tt1 = A[11] ^ A[16]; - tt0 ^= A[21] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[ 4] ^ A[ 9]; - tt3 = A[14] ^ A[19]; - tt0 ^= A[24]; - tt2 ^= tt3; - t0 = tt0 ^ tt2; - - tt0 = A[ 2] ^ A[ 7]; - tt1 = A[12] ^ A[17]; - tt0 ^= A[22] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[ 0] ^ A[ 5]; - tt3 = A[10] ^ A[15]; - tt0 ^= A[20]; - tt2 ^= tt3; - t1 = tt0 ^ tt2; - - tt0 = A[ 3] ^ A[ 8]; - tt1 = A[13] ^ A[18]; - tt0 ^= A[23] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[ 1] ^ A[ 6]; - tt3 = A[11] ^ A[16]; - tt0 ^= A[21]; - tt2 ^= tt3; - t2 = tt0 ^ tt2; - - tt0 = A[ 4] ^ A[ 9]; - tt1 = A[14] ^ A[19]; - tt0 ^= A[24] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[ 2] ^ A[ 7]; - tt3 = A[12] ^ A[17]; - tt0 ^= A[22]; - tt2 ^= tt3; - t3 = tt0 ^ tt2; - - tt0 = A[ 0] ^ A[ 5]; - tt1 = A[10] ^ A[15]; - tt0 ^= A[20] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[ 3] ^ A[ 8]; - tt3 = A[13] ^ A[18]; - tt0 ^= A[23]; - tt2 ^= tt3; - t4 = tt0 ^ tt2; - - A[ 0] = A[ 0] ^ t0; - A[ 5] = A[ 5] ^ t0; - A[10] = A[10] ^ t0; - A[15] = A[15] ^ t0; - A[20] = A[20] ^ t0; - A[ 1] = A[ 1] ^ t1; - A[ 6] = A[ 6] ^ t1; - A[11] = A[11] ^ t1; - A[16] = A[16] ^ t1; - A[21] = A[21] ^ t1; - A[ 2] = A[ 2] ^ t2; - A[ 7] = A[ 7] ^ t2; - A[12] = A[12] ^ t2; - A[17] = A[17] ^ t2; - A[22] = A[22] ^ t2; - A[ 3] = A[ 3] ^ t3; - A[ 8] = A[ 8] ^ t3; - A[13] = A[13] ^ t3; - A[18] = A[18] ^ t3; - A[23] = A[23] ^ t3; - A[ 4] = A[ 4] ^ t4; - A[ 9] = A[ 9] ^ t4; - A[14] = A[14] ^ t4; - A[19] = A[19] ^ t4; - A[24] = A[24] ^ t4; - A[ 5] = (A[ 5] << 36) | (A[ 5] >>> (64 - 36)); - A[10] = (A[10] << 3) | (A[10] >>> (64 - 3)); - A[15] = (A[15] << 41) | (A[15] >>> (64 - 41)); - A[20] = (A[20] << 18) | (A[20] >>> (64 - 18)); - A[ 1] = (A[ 1] << 1) | (A[ 1] >>> (64 - 1)); - A[ 6] = (A[ 6] << 44) | (A[ 6] >>> (64 - 44)); - A[11] = (A[11] << 10) | (A[11] >>> (64 - 10)); - A[16] = (A[16] << 45) | (A[16] >>> (64 - 45)); - A[21] = (A[21] << 2) | (A[21] >>> (64 - 2)); - A[ 2] = (A[ 2] << 62) | (A[ 2] >>> (64 - 62)); - A[ 7] = (A[ 7] << 6) | (A[ 7] >>> (64 - 6)); - A[12] = (A[12] << 43) | (A[12] >>> (64 - 43)); - A[17] = (A[17] << 15) | (A[17] >>> (64 - 15)); - A[22] = (A[22] << 61) | (A[22] >>> (64 - 61)); - A[ 3] = (A[ 3] << 28) | (A[ 3] >>> (64 - 28)); - A[ 8] = (A[ 8] << 55) | (A[ 8] >>> (64 - 55)); - A[13] = (A[13] << 25) | (A[13] >>> (64 - 25)); - A[18] = (A[18] << 21) | (A[18] >>> (64 - 21)); - A[23] = (A[23] << 56) | (A[23] >>> (64 - 56)); - A[ 4] = (A[ 4] << 27) | (A[ 4] >>> (64 - 27)); - A[ 9] = (A[ 9] << 20) | (A[ 9] >>> (64 - 20)); - A[14] = (A[14] << 39) | (A[14] >>> (64 - 39)); - A[19] = (A[19] << 8) | (A[19] >>> (64 - 8)); - A[24] = (A[24] << 14) | (A[24] >>> (64 - 14)); - bnn = ~A[12]; - kt = A[ 6] | A[12]; - c0 = A[ 0] ^ kt; - kt = bnn | A[18]; - c1 = A[ 6] ^ kt; - kt = A[18] & A[24]; - c2 = A[12] ^ kt; - kt = A[24] | A[ 0]; - c3 = A[18] ^ kt; - kt = A[ 0] & A[ 6]; - c4 = A[24] ^ kt; - A[ 0] = c0; - A[ 6] = c1; - A[12] = c2; - A[18] = c3; - A[24] = c4; - bnn = ~A[22]; - kt = A[ 9] | A[10]; - c0 = A[ 3] ^ kt; - kt = A[10] & A[16]; - c1 = A[ 9] ^ kt; - kt = A[16] | bnn; - c2 = A[10] ^ kt; - kt = A[22] | A[ 3]; - c3 = A[16] ^ kt; - kt = A[ 3] & A[ 9]; - c4 = A[22] ^ kt; - A[ 3] = c0; - A[ 9] = c1; - A[10] = c2; - A[16] = c3; - A[22] = c4; - bnn = ~A[19]; - kt = A[ 7] | A[13]; - c0 = A[ 1] ^ kt; - kt = A[13] & A[19]; - c1 = A[ 7] ^ kt; - kt = bnn & A[20]; - c2 = A[13] ^ kt; - kt = A[20] | A[ 1]; - c3 = bnn ^ kt; - kt = A[ 1] & A[ 7]; - c4 = A[20] ^ kt; - A[ 1] = c0; - A[ 7] = c1; - A[13] = c2; - A[19] = c3; - A[20] = c4; - bnn = ~A[17]; - kt = A[ 5] & A[11]; - c0 = A[ 4] ^ kt; - kt = A[11] | A[17]; - c1 = A[ 5] ^ kt; - kt = bnn | A[23]; - c2 = A[11] ^ kt; - kt = A[23] & A[ 4]; - c3 = bnn ^ kt; - kt = A[ 4] | A[ 5]; - c4 = A[23] ^ kt; - A[ 4] = c0; - A[ 5] = c1; - A[11] = c2; - A[17] = c3; - A[23] = c4; - bnn = ~A[ 8]; - kt = bnn & A[14]; - c0 = A[ 2] ^ kt; - kt = A[14] | A[15]; - c1 = bnn ^ kt; - kt = A[15] & A[21]; - c2 = A[14] ^ kt; - kt = A[21] | A[ 2]; - c3 = A[15] ^ kt; - kt = A[ 2] & A[ 8]; - c4 = A[21] ^ kt; - A[ 2] = c0; - A[ 8] = c1; - A[14] = c2; - A[15] = c3; - A[21] = c4; - A[ 0] = A[ 0] ^ RC[j + 0]; - - tt0 = A[ 6] ^ A[ 9]; - tt1 = A[ 7] ^ A[ 5]; - tt0 ^= A[ 8] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[24] ^ A[22]; - tt3 = A[20] ^ A[23]; - tt0 ^= A[21]; - tt2 ^= tt3; - t0 = tt0 ^ tt2; - - tt0 = A[12] ^ A[10]; - tt1 = A[13] ^ A[11]; - tt0 ^= A[14] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[ 0] ^ A[ 3]; - tt3 = A[ 1] ^ A[ 4]; - tt0 ^= A[ 2]; - tt2 ^= tt3; - t1 = tt0 ^ tt2; - - tt0 = A[18] ^ A[16]; - tt1 = A[19] ^ A[17]; - tt0 ^= A[15] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[ 6] ^ A[ 9]; - tt3 = A[ 7] ^ A[ 5]; - tt0 ^= A[ 8]; - tt2 ^= tt3; - t2 = tt0 ^ tt2; - - tt0 = A[24] ^ A[22]; - tt1 = A[20] ^ A[23]; - tt0 ^= A[21] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[12] ^ A[10]; - tt3 = A[13] ^ A[11]; - tt0 ^= A[14]; - tt2 ^= tt3; - t3 = tt0 ^ tt2; - - tt0 = A[ 0] ^ A[ 3]; - tt1 = A[ 1] ^ A[ 4]; - tt0 ^= A[ 2] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[18] ^ A[16]; - tt3 = A[19] ^ A[17]; - tt0 ^= A[15]; - tt2 ^= tt3; - t4 = tt0 ^ tt2; - - A[ 0] = A[ 0] ^ t0; - A[ 3] = A[ 3] ^ t0; - A[ 1] = A[ 1] ^ t0; - A[ 4] = A[ 4] ^ t0; - A[ 2] = A[ 2] ^ t0; - A[ 6] = A[ 6] ^ t1; - A[ 9] = A[ 9] ^ t1; - A[ 7] = A[ 7] ^ t1; - A[ 5] = A[ 5] ^ t1; - A[ 8] = A[ 8] ^ t1; - A[12] = A[12] ^ t2; - A[10] = A[10] ^ t2; - A[13] = A[13] ^ t2; - A[11] = A[11] ^ t2; - A[14] = A[14] ^ t2; - A[18] = A[18] ^ t3; - A[16] = A[16] ^ t3; - A[19] = A[19] ^ t3; - A[17] = A[17] ^ t3; - A[15] = A[15] ^ t3; - A[24] = A[24] ^ t4; - A[22] = A[22] ^ t4; - A[20] = A[20] ^ t4; - A[23] = A[23] ^ t4; - A[21] = A[21] ^ t4; - A[ 3] = (A[ 3] << 36) | (A[ 3] >>> (64 - 36)); - A[ 1] = (A[ 1] << 3) | (A[ 1] >>> (64 - 3)); - A[ 4] = (A[ 4] << 41) | (A[ 4] >>> (64 - 41)); - A[ 2] = (A[ 2] << 18) | (A[ 2] >>> (64 - 18)); - A[ 6] = (A[ 6] << 1) | (A[ 6] >>> (64 - 1)); - A[ 9] = (A[ 9] << 44) | (A[ 9] >>> (64 - 44)); - A[ 7] = (A[ 7] << 10) | (A[ 7] >>> (64 - 10)); - A[ 5] = (A[ 5] << 45) | (A[ 5] >>> (64 - 45)); - A[ 8] = (A[ 8] << 2) | (A[ 8] >>> (64 - 2)); - A[12] = (A[12] << 62) | (A[12] >>> (64 - 62)); - A[10] = (A[10] << 6) | (A[10] >>> (64 - 6)); - A[13] = (A[13] << 43) | (A[13] >>> (64 - 43)); - A[11] = (A[11] << 15) | (A[11] >>> (64 - 15)); - A[14] = (A[14] << 61) | (A[14] >>> (64 - 61)); - A[18] = (A[18] << 28) | (A[18] >>> (64 - 28)); - A[16] = (A[16] << 55) | (A[16] >>> (64 - 55)); - A[19] = (A[19] << 25) | (A[19] >>> (64 - 25)); - A[17] = (A[17] << 21) | (A[17] >>> (64 - 21)); - A[15] = (A[15] << 56) | (A[15] >>> (64 - 56)); - A[24] = (A[24] << 27) | (A[24] >>> (64 - 27)); - A[22] = (A[22] << 20) | (A[22] >>> (64 - 20)); - A[20] = (A[20] << 39) | (A[20] >>> (64 - 39)); - A[23] = (A[23] << 8) | (A[23] >>> (64 - 8)); - A[21] = (A[21] << 14) | (A[21] >>> (64 - 14)); - bnn = ~A[13]; - kt = A[ 9] | A[13]; - c0 = A[ 0] ^ kt; - kt = bnn | A[17]; - c1 = A[ 9] ^ kt; - kt = A[17] & A[21]; - c2 = A[13] ^ kt; - kt = A[21] | A[ 0]; - c3 = A[17] ^ kt; - kt = A[ 0] & A[ 9]; - c4 = A[21] ^ kt; - A[ 0] = c0; - A[ 9] = c1; - A[13] = c2; - A[17] = c3; - A[21] = c4; - bnn = ~A[14]; - kt = A[22] | A[ 1]; - c0 = A[18] ^ kt; - kt = A[ 1] & A[ 5]; - c1 = A[22] ^ kt; - kt = A[ 5] | bnn; - c2 = A[ 1] ^ kt; - kt = A[14] | A[18]; - c3 = A[ 5] ^ kt; - kt = A[18] & A[22]; - c4 = A[14] ^ kt; - A[18] = c0; - A[22] = c1; - A[ 1] = c2; - A[ 5] = c3; - A[14] = c4; - bnn = ~A[23]; - kt = A[10] | A[19]; - c0 = A[ 6] ^ kt; - kt = A[19] & A[23]; - c1 = A[10] ^ kt; - kt = bnn & A[ 2]; - c2 = A[19] ^ kt; - kt = A[ 2] | A[ 6]; - c3 = bnn ^ kt; - kt = A[ 6] & A[10]; - c4 = A[ 2] ^ kt; - A[ 6] = c0; - A[10] = c1; - A[19] = c2; - A[23] = c3; - A[ 2] = c4; - bnn = ~A[11]; - kt = A[ 3] & A[ 7]; - c0 = A[24] ^ kt; - kt = A[ 7] | A[11]; - c1 = A[ 3] ^ kt; - kt = bnn | A[15]; - c2 = A[ 7] ^ kt; - kt = A[15] & A[24]; - c3 = bnn ^ kt; - kt = A[24] | A[ 3]; - c4 = A[15] ^ kt; - A[24] = c0; - A[ 3] = c1; - A[ 7] = c2; - A[11] = c3; - A[15] = c4; - bnn = ~A[16]; - kt = bnn & A[20]; - c0 = A[12] ^ kt; - kt = A[20] | A[ 4]; - c1 = bnn ^ kt; - kt = A[ 4] & A[ 8]; - c2 = A[20] ^ kt; - kt = A[ 8] | A[12]; - c3 = A[ 4] ^ kt; - kt = A[12] & A[16]; - c4 = A[ 8] ^ kt; - A[12] = c0; - A[16] = c1; - A[20] = c2; - A[ 4] = c3; - A[ 8] = c4; - A[ 0] = A[ 0] ^ RC[j + 1]; - t = A[ 5]; - A[ 5] = A[18]; - A[18] = A[11]; - A[11] = A[10]; - A[10] = A[ 6]; - A[ 6] = A[22]; - A[22] = A[20]; - A[20] = A[12]; - A[12] = A[19]; - A[19] = A[15]; - A[15] = A[24]; - A[24] = A[ 8]; - A[ 8] = t; - t = A[ 1]; - A[ 1] = A[ 9]; - A[ 9] = A[14]; - A[14] = A[ 2]; - A[ 2] = A[13]; - A[13] = A[23]; - A[23] = A[ 4]; - A[ 4] = A[21]; - A[21] = A[16]; - A[16] = A[ 3]; - A[ 3] = A[17]; - A[17] = A[ 7]; - A[ 7] = t; - } - } - - protected void doPadding(byte[] out, int off) - { - int ptr = flush(); - byte[] buf = getBlockBuffer(); - if ((ptr + 1) == buf.length) { - buf[ptr] = (byte)0x81; - } else { - buf[ptr] = (byte)0x01; - for (int i = ptr + 1; i < (buf.length - 1); i ++) - buf[i] = 0; - buf[buf.length - 1] = (byte)0x80; - } - processBlock(buf); - A[ 1] = ~A[ 1]; - A[ 2] = ~A[ 2]; - A[ 8] = ~A[ 8]; - A[12] = ~A[12]; - A[17] = ~A[17]; - A[20] = ~A[20]; - int dlen = engineGetDigestLength(); - for (int i = 0; i < dlen; i += 8) - encodeLELong(A[i >>> 3], tmpOut, i); - System.arraycopy(tmpOut, 0, out, off, dlen); - } - - protected void doInit() - { - A = new long[25]; - tmpOut = new byte[(engineGetDigestLength() + 7) & ~7]; - doReset(); - } - - public int getBlockLength() - { - return 200 - 2 * engineGetDigestLength(); - } - - private final void doReset() - { - for (int i = 0; i < 25; i ++) - A[i] = 0; - A[ 1] = 0xFFFFFFFFFFFFFFFFL; - A[ 2] = 0xFFFFFFFFFFFFFFFFL; - A[ 8] = 0xFFFFFFFFFFFFFFFFL; - A[12] = 0xFFFFFFFFFFFFFFFFL; - A[17] = 0xFFFFFFFFFFFFFFFFL; - A[20] = 0xFFFFFFFFFFFFFFFFL; - } - - - protected Digest copyState(KeccakCore dst) - { - System.arraycopy(A, 0, dst.A, 0, 25); - return super.copyState(dst); - } - - public String toString() - { - return "Keccak-" + (engineGetDigestLength() << 3); - } -} +package com.tangem.wallet; + + +/** + * Created by Ilia on 18.12.2017. + */ +abstract class KeccakCore extends DigestEngine{ + KeccakCore(String alg) + { + super(alg); + } + + private long[] A; + private byte[] tmpOut; + + private static final long[] RC = { + 0x0000000000000001L, 0x0000000000008082L, + 0x800000000000808AL, 0x8000000080008000L, + 0x000000000000808BL, 0x0000000080000001L, + 0x8000000080008081L, 0x8000000000008009L, + 0x000000000000008AL, 0x0000000000000088L, + 0x0000000080008009L, 0x000000008000000AL, + 0x000000008000808BL, 0x800000000000008BL, + 0x8000000000008089L, 0x8000000000008003L, + 0x8000000000008002L, 0x8000000000000080L, + 0x000000000000800AL, 0x800000008000000AL, + 0x8000000080008081L, 0x8000000000008080L, + 0x0000000080000001L, 0x8000000080008008L + }; + + /** + * Encode the 64-bit word {@code val} into the array + * {@code buf} at offset {@code off}, in little-endian + * convention (least significant byte first). + * + * @param val the value to encode + * @param buf the destination buffer + * @param off the destination offset + */ + private static void encodeLELong(long val, byte[] buf, int off) + { + buf[off + 0] = (byte)val; + buf[off + 1] = (byte)(val >>> 8); + buf[off + 2] = (byte)(val >>> 16); + buf[off + 3] = (byte)(val >>> 24); + buf[off + 4] = (byte)(val >>> 32); + buf[off + 5] = (byte)(val >>> 40); + buf[off + 6] = (byte)(val >>> 48); + buf[off + 7] = (byte)(val >>> 56); + } + + /** + * Decode a 64-bit little-endian word from the array {@code buf} + * at offset {@code off}. + * + * @param buf the source buffer + * @param off the source offset + * @return the decoded value + */ + private static long decodeLELong(byte[] buf, int off) + { + return (buf[off + 0] & 0xFFL) + | ((buf[off + 1] & 0xFFL) << 8) + | ((buf[off + 2] & 0xFFL) << 16) + | ((buf[off + 3] & 0xFFL) << 24) + | ((buf[off + 4] & 0xFFL) << 32) + | ((buf[off + 5] & 0xFFL) << 40) + | ((buf[off + 6] & 0xFFL) << 48) + | ((buf[off + 7] & 0xFFL) << 56); + } + + protected void engineReset() + { + doReset(); + } + + protected void processBlock(byte[] data) + { + /* Input block */ + for (int i = 0; i < data.length; i += 8) + A[i >>> 3] ^= decodeLELong(data, i); + + long t0, t1, t2, t3, t4; + long tt0, tt1, tt2, tt3, tt4; + long t, kt; + long c0, c1, c2, c3, c4, bnn; + + /* + * Unrolling four rounds kills performance big time + * on Intel x86 Core2, in both 32-bit and 64-bit modes + * (less than 1 MB/s instead of 55 MB/s on x86-64). + * Unrolling two rounds appears to be fine. + */ + for (int j = 0; j < 24; j += 2) { + + tt0 = A[ 1] ^ A[ 6]; + tt1 = A[11] ^ A[16]; + tt0 ^= A[21] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 4] ^ A[ 9]; + tt3 = A[14] ^ A[19]; + tt0 ^= A[24]; + tt2 ^= tt3; + t0 = tt0 ^ tt2; + + tt0 = A[ 2] ^ A[ 7]; + tt1 = A[12] ^ A[17]; + tt0 ^= A[22] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 0] ^ A[ 5]; + tt3 = A[10] ^ A[15]; + tt0 ^= A[20]; + tt2 ^= tt3; + t1 = tt0 ^ tt2; + + tt0 = A[ 3] ^ A[ 8]; + tt1 = A[13] ^ A[18]; + tt0 ^= A[23] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 1] ^ A[ 6]; + tt3 = A[11] ^ A[16]; + tt0 ^= A[21]; + tt2 ^= tt3; + t2 = tt0 ^ tt2; + + tt0 = A[ 4] ^ A[ 9]; + tt1 = A[14] ^ A[19]; + tt0 ^= A[24] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 2] ^ A[ 7]; + tt3 = A[12] ^ A[17]; + tt0 ^= A[22]; + tt2 ^= tt3; + t3 = tt0 ^ tt2; + + tt0 = A[ 0] ^ A[ 5]; + tt1 = A[10] ^ A[15]; + tt0 ^= A[20] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 3] ^ A[ 8]; + tt3 = A[13] ^ A[18]; + tt0 ^= A[23]; + tt2 ^= tt3; + t4 = tt0 ^ tt2; + + A[ 0] = A[ 0] ^ t0; + A[ 5] = A[ 5] ^ t0; + A[10] = A[10] ^ t0; + A[15] = A[15] ^ t0; + A[20] = A[20] ^ t0; + A[ 1] = A[ 1] ^ t1; + A[ 6] = A[ 6] ^ t1; + A[11] = A[11] ^ t1; + A[16] = A[16] ^ t1; + A[21] = A[21] ^ t1; + A[ 2] = A[ 2] ^ t2; + A[ 7] = A[ 7] ^ t2; + A[12] = A[12] ^ t2; + A[17] = A[17] ^ t2; + A[22] = A[22] ^ t2; + A[ 3] = A[ 3] ^ t3; + A[ 8] = A[ 8] ^ t3; + A[13] = A[13] ^ t3; + A[18] = A[18] ^ t3; + A[23] = A[23] ^ t3; + A[ 4] = A[ 4] ^ t4; + A[ 9] = A[ 9] ^ t4; + A[14] = A[14] ^ t4; + A[19] = A[19] ^ t4; + A[24] = A[24] ^ t4; + A[ 5] = (A[ 5] << 36) | (A[ 5] >>> (64 - 36)); + A[10] = (A[10] << 3) | (A[10] >>> (64 - 3)); + A[15] = (A[15] << 41) | (A[15] >>> (64 - 41)); + A[20] = (A[20] << 18) | (A[20] >>> (64 - 18)); + A[ 1] = (A[ 1] << 1) | (A[ 1] >>> (64 - 1)); + A[ 6] = (A[ 6] << 44) | (A[ 6] >>> (64 - 44)); + A[11] = (A[11] << 10) | (A[11] >>> (64 - 10)); + A[16] = (A[16] << 45) | (A[16] >>> (64 - 45)); + A[21] = (A[21] << 2) | (A[21] >>> (64 - 2)); + A[ 2] = (A[ 2] << 62) | (A[ 2] >>> (64 - 62)); + A[ 7] = (A[ 7] << 6) | (A[ 7] >>> (64 - 6)); + A[12] = (A[12] << 43) | (A[12] >>> (64 - 43)); + A[17] = (A[17] << 15) | (A[17] >>> (64 - 15)); + A[22] = (A[22] << 61) | (A[22] >>> (64 - 61)); + A[ 3] = (A[ 3] << 28) | (A[ 3] >>> (64 - 28)); + A[ 8] = (A[ 8] << 55) | (A[ 8] >>> (64 - 55)); + A[13] = (A[13] << 25) | (A[13] >>> (64 - 25)); + A[18] = (A[18] << 21) | (A[18] >>> (64 - 21)); + A[23] = (A[23] << 56) | (A[23] >>> (64 - 56)); + A[ 4] = (A[ 4] << 27) | (A[ 4] >>> (64 - 27)); + A[ 9] = (A[ 9] << 20) | (A[ 9] >>> (64 - 20)); + A[14] = (A[14] << 39) | (A[14] >>> (64 - 39)); + A[19] = (A[19] << 8) | (A[19] >>> (64 - 8)); + A[24] = (A[24] << 14) | (A[24] >>> (64 - 14)); + bnn = ~A[12]; + kt = A[ 6] | A[12]; + c0 = A[ 0] ^ kt; + kt = bnn | A[18]; + c1 = A[ 6] ^ kt; + kt = A[18] & A[24]; + c2 = A[12] ^ kt; + kt = A[24] | A[ 0]; + c3 = A[18] ^ kt; + kt = A[ 0] & A[ 6]; + c4 = A[24] ^ kt; + A[ 0] = c0; + A[ 6] = c1; + A[12] = c2; + A[18] = c3; + A[24] = c4; + bnn = ~A[22]; + kt = A[ 9] | A[10]; + c0 = A[ 3] ^ kt; + kt = A[10] & A[16]; + c1 = A[ 9] ^ kt; + kt = A[16] | bnn; + c2 = A[10] ^ kt; + kt = A[22] | A[ 3]; + c3 = A[16] ^ kt; + kt = A[ 3] & A[ 9]; + c4 = A[22] ^ kt; + A[ 3] = c0; + A[ 9] = c1; + A[10] = c2; + A[16] = c3; + A[22] = c4; + bnn = ~A[19]; + kt = A[ 7] | A[13]; + c0 = A[ 1] ^ kt; + kt = A[13] & A[19]; + c1 = A[ 7] ^ kt; + kt = bnn & A[20]; + c2 = A[13] ^ kt; + kt = A[20] | A[ 1]; + c3 = bnn ^ kt; + kt = A[ 1] & A[ 7]; + c4 = A[20] ^ kt; + A[ 1] = c0; + A[ 7] = c1; + A[13] = c2; + A[19] = c3; + A[20] = c4; + bnn = ~A[17]; + kt = A[ 5] & A[11]; + c0 = A[ 4] ^ kt; + kt = A[11] | A[17]; + c1 = A[ 5] ^ kt; + kt = bnn | A[23]; + c2 = A[11] ^ kt; + kt = A[23] & A[ 4]; + c3 = bnn ^ kt; + kt = A[ 4] | A[ 5]; + c4 = A[23] ^ kt; + A[ 4] = c0; + A[ 5] = c1; + A[11] = c2; + A[17] = c3; + A[23] = c4; + bnn = ~A[ 8]; + kt = bnn & A[14]; + c0 = A[ 2] ^ kt; + kt = A[14] | A[15]; + c1 = bnn ^ kt; + kt = A[15] & A[21]; + c2 = A[14] ^ kt; + kt = A[21] | A[ 2]; + c3 = A[15] ^ kt; + kt = A[ 2] & A[ 8]; + c4 = A[21] ^ kt; + A[ 2] = c0; + A[ 8] = c1; + A[14] = c2; + A[15] = c3; + A[21] = c4; + A[ 0] = A[ 0] ^ RC[j + 0]; + + tt0 = A[ 6] ^ A[ 9]; + tt1 = A[ 7] ^ A[ 5]; + tt0 ^= A[ 8] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[24] ^ A[22]; + tt3 = A[20] ^ A[23]; + tt0 ^= A[21]; + tt2 ^= tt3; + t0 = tt0 ^ tt2; + + tt0 = A[12] ^ A[10]; + tt1 = A[13] ^ A[11]; + tt0 ^= A[14] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 0] ^ A[ 3]; + tt3 = A[ 1] ^ A[ 4]; + tt0 ^= A[ 2]; + tt2 ^= tt3; + t1 = tt0 ^ tt2; + + tt0 = A[18] ^ A[16]; + tt1 = A[19] ^ A[17]; + tt0 ^= A[15] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 6] ^ A[ 9]; + tt3 = A[ 7] ^ A[ 5]; + tt0 ^= A[ 8]; + tt2 ^= tt3; + t2 = tt0 ^ tt2; + + tt0 = A[24] ^ A[22]; + tt1 = A[20] ^ A[23]; + tt0 ^= A[21] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[12] ^ A[10]; + tt3 = A[13] ^ A[11]; + tt0 ^= A[14]; + tt2 ^= tt3; + t3 = tt0 ^ tt2; + + tt0 = A[ 0] ^ A[ 3]; + tt1 = A[ 1] ^ A[ 4]; + tt0 ^= A[ 2] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[18] ^ A[16]; + tt3 = A[19] ^ A[17]; + tt0 ^= A[15]; + tt2 ^= tt3; + t4 = tt0 ^ tt2; + + A[ 0] = A[ 0] ^ t0; + A[ 3] = A[ 3] ^ t0; + A[ 1] = A[ 1] ^ t0; + A[ 4] = A[ 4] ^ t0; + A[ 2] = A[ 2] ^ t0; + A[ 6] = A[ 6] ^ t1; + A[ 9] = A[ 9] ^ t1; + A[ 7] = A[ 7] ^ t1; + A[ 5] = A[ 5] ^ t1; + A[ 8] = A[ 8] ^ t1; + A[12] = A[12] ^ t2; + A[10] = A[10] ^ t2; + A[13] = A[13] ^ t2; + A[11] = A[11] ^ t2; + A[14] = A[14] ^ t2; + A[18] = A[18] ^ t3; + A[16] = A[16] ^ t3; + A[19] = A[19] ^ t3; + A[17] = A[17] ^ t3; + A[15] = A[15] ^ t3; + A[24] = A[24] ^ t4; + A[22] = A[22] ^ t4; + A[20] = A[20] ^ t4; + A[23] = A[23] ^ t4; + A[21] = A[21] ^ t4; + A[ 3] = (A[ 3] << 36) | (A[ 3] >>> (64 - 36)); + A[ 1] = (A[ 1] << 3) | (A[ 1] >>> (64 - 3)); + A[ 4] = (A[ 4] << 41) | (A[ 4] >>> (64 - 41)); + A[ 2] = (A[ 2] << 18) | (A[ 2] >>> (64 - 18)); + A[ 6] = (A[ 6] << 1) | (A[ 6] >>> (64 - 1)); + A[ 9] = (A[ 9] << 44) | (A[ 9] >>> (64 - 44)); + A[ 7] = (A[ 7] << 10) | (A[ 7] >>> (64 - 10)); + A[ 5] = (A[ 5] << 45) | (A[ 5] >>> (64 - 45)); + A[ 8] = (A[ 8] << 2) | (A[ 8] >>> (64 - 2)); + A[12] = (A[12] << 62) | (A[12] >>> (64 - 62)); + A[10] = (A[10] << 6) | (A[10] >>> (64 - 6)); + A[13] = (A[13] << 43) | (A[13] >>> (64 - 43)); + A[11] = (A[11] << 15) | (A[11] >>> (64 - 15)); + A[14] = (A[14] << 61) | (A[14] >>> (64 - 61)); + A[18] = (A[18] << 28) | (A[18] >>> (64 - 28)); + A[16] = (A[16] << 55) | (A[16] >>> (64 - 55)); + A[19] = (A[19] << 25) | (A[19] >>> (64 - 25)); + A[17] = (A[17] << 21) | (A[17] >>> (64 - 21)); + A[15] = (A[15] << 56) | (A[15] >>> (64 - 56)); + A[24] = (A[24] << 27) | (A[24] >>> (64 - 27)); + A[22] = (A[22] << 20) | (A[22] >>> (64 - 20)); + A[20] = (A[20] << 39) | (A[20] >>> (64 - 39)); + A[23] = (A[23] << 8) | (A[23] >>> (64 - 8)); + A[21] = (A[21] << 14) | (A[21] >>> (64 - 14)); + bnn = ~A[13]; + kt = A[ 9] | A[13]; + c0 = A[ 0] ^ kt; + kt = bnn | A[17]; + c1 = A[ 9] ^ kt; + kt = A[17] & A[21]; + c2 = A[13] ^ kt; + kt = A[21] | A[ 0]; + c3 = A[17] ^ kt; + kt = A[ 0] & A[ 9]; + c4 = A[21] ^ kt; + A[ 0] = c0; + A[ 9] = c1; + A[13] = c2; + A[17] = c3; + A[21] = c4; + bnn = ~A[14]; + kt = A[22] | A[ 1]; + c0 = A[18] ^ kt; + kt = A[ 1] & A[ 5]; + c1 = A[22] ^ kt; + kt = A[ 5] | bnn; + c2 = A[ 1] ^ kt; + kt = A[14] | A[18]; + c3 = A[ 5] ^ kt; + kt = A[18] & A[22]; + c4 = A[14] ^ kt; + A[18] = c0; + A[22] = c1; + A[ 1] = c2; + A[ 5] = c3; + A[14] = c4; + bnn = ~A[23]; + kt = A[10] | A[19]; + c0 = A[ 6] ^ kt; + kt = A[19] & A[23]; + c1 = A[10] ^ kt; + kt = bnn & A[ 2]; + c2 = A[19] ^ kt; + kt = A[ 2] | A[ 6]; + c3 = bnn ^ kt; + kt = A[ 6] & A[10]; + c4 = A[ 2] ^ kt; + A[ 6] = c0; + A[10] = c1; + A[19] = c2; + A[23] = c3; + A[ 2] = c4; + bnn = ~A[11]; + kt = A[ 3] & A[ 7]; + c0 = A[24] ^ kt; + kt = A[ 7] | A[11]; + c1 = A[ 3] ^ kt; + kt = bnn | A[15]; + c2 = A[ 7] ^ kt; + kt = A[15] & A[24]; + c3 = bnn ^ kt; + kt = A[24] | A[ 3]; + c4 = A[15] ^ kt; + A[24] = c0; + A[ 3] = c1; + A[ 7] = c2; + A[11] = c3; + A[15] = c4; + bnn = ~A[16]; + kt = bnn & A[20]; + c0 = A[12] ^ kt; + kt = A[20] | A[ 4]; + c1 = bnn ^ kt; + kt = A[ 4] & A[ 8]; + c2 = A[20] ^ kt; + kt = A[ 8] | A[12]; + c3 = A[ 4] ^ kt; + kt = A[12] & A[16]; + c4 = A[ 8] ^ kt; + A[12] = c0; + A[16] = c1; + A[20] = c2; + A[ 4] = c3; + A[ 8] = c4; + A[ 0] = A[ 0] ^ RC[j + 1]; + t = A[ 5]; + A[ 5] = A[18]; + A[18] = A[11]; + A[11] = A[10]; + A[10] = A[ 6]; + A[ 6] = A[22]; + A[22] = A[20]; + A[20] = A[12]; + A[12] = A[19]; + A[19] = A[15]; + A[15] = A[24]; + A[24] = A[ 8]; + A[ 8] = t; + t = A[ 1]; + A[ 1] = A[ 9]; + A[ 9] = A[14]; + A[14] = A[ 2]; + A[ 2] = A[13]; + A[13] = A[23]; + A[23] = A[ 4]; + A[ 4] = A[21]; + A[21] = A[16]; + A[16] = A[ 3]; + A[ 3] = A[17]; + A[17] = A[ 7]; + A[ 7] = t; + } + } + + protected void doPadding(byte[] out, int off) + { + int ptr = flush(); + byte[] buf = getBlockBuffer(); + if ((ptr + 1) == buf.length) { + buf[ptr] = (byte)0x81; + } else { + buf[ptr] = (byte)0x01; + for (int i = ptr + 1; i < (buf.length - 1); i ++) + buf[i] = 0; + buf[buf.length - 1] = (byte)0x80; + } + processBlock(buf); + A[ 1] = ~A[ 1]; + A[ 2] = ~A[ 2]; + A[ 8] = ~A[ 8]; + A[12] = ~A[12]; + A[17] = ~A[17]; + A[20] = ~A[20]; + int dlen = engineGetDigestLength(); + for (int i = 0; i < dlen; i += 8) + encodeLELong(A[i >>> 3], tmpOut, i); + System.arraycopy(tmpOut, 0, out, off, dlen); + } + + protected void doInit() + { + A = new long[25]; + tmpOut = new byte[(engineGetDigestLength() + 7) & ~7]; + doReset(); + } + + public int getBlockLength() + { + return 200 - 2 * engineGetDigestLength(); + } + + private final void doReset() + { + for (int i = 0; i < 25; i ++) + A[i] = 0; + A[ 1] = 0xFFFFFFFFFFFFFFFFL; + A[ 2] = 0xFFFFFFFFFFFFFFFFL; + A[ 8] = 0xFFFFFFFFFFFFFFFFL; + A[12] = 0xFFFFFFFFFFFFFFFFL; + A[17] = 0xFFFFFFFFFFFFFFFFL; + A[20] = 0xFFFFFFFFFFFFFFFFL; + } + + + protected Digest copyState(KeccakCore dst) + { + System.arraycopy(A, 0, dst.A, 0, 25); + return super.copyState(dst); + } + + public String toString() + { + return "Keccak-" + (engineGetDigestLength() << 3); + } +} diff --git a/app/src/main/java/com/tangem/wallet/LastSignStorage.java b/app/src/main/java/com/tangem/wallet/LastSignStorage.java index 73dc61b561..b12d6df8de 100644 --- a/app/src/main/java/com/tangem/wallet/LastSignStorage.java +++ b/app/src/main/java/com/tangem/wallet/LastSignStorage.java @@ -1,146 +1,146 @@ -package com.tangem.wallet; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.util.ArrayMap; -import android.util.ArraySet; - -import java.lang.reflect.Array; -import java.util.Date; -import java.util.Map; -import java.util.Set; - -/** - * Created by dvol on 30.10.2017. - */ - -public class LastSignStorage { - - private static SharedPreferences sharedPreferences=null; - - private static Set cards = new ArraySet<>(); - private static Map dates = new ArrayMap<>(); - private static Map txCol = new ArrayMap<>(); - private static Map txCompleteCol = new ArrayMap<>(); - - static void Init(Context context) { - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - cards=sharedPreferences.getStringSet("LastSign_Cards", cards); - for (int i = 0; i < cards.size(); i++) { - String wallet = cards.toArray()[i].toString(); - Date dt = new Date(); - dt.setTime(sharedPreferences.getLong("LastSign_" + wallet, 0)); - dates.put(wallet, dt); - } - } - - public static boolean needInit() { - return sharedPreferences==null; - } - - static class CompleteTx - { - public String TX; - public boolean isComplete; - } - public static Map GetTxList() - { - Set wallets=sharedPreferences.getStringSet("LastSign_Cards", cards); - Map txList = new ArrayMap<>(); - - for (int i = 0; i < wallets.size(); i++) { - String wallet = wallets.toArray()[i].toString(); - String tx = sharedPreferences.getString("LastSignTX_" + wallet, ""); - boolean complete = sharedPreferences.getBoolean("LastSignTXComplete_" + wallet, false); - CompleteTx txComplete = new CompleteTx(); - txComplete.isComplete = complete; - txComplete.TX = tx; - txList.put(wallet, txComplete); - } - - return txList; - } - public static Date getLastSignDate(String wallet) { - if (dates.containsKey(wallet)) return dates.get(wallet); - return null; - } - - public static void setLastSignDate(String wallet, Date date) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - if (!cards.contains(wallet)) { - cards.add(wallet); - editor.putStringSet("LastSign_Cards", cards); - } - dates.put(wallet, date); - editor.putLong("LastSign_" + wallet, date.getTime()); - editor.apply(); - } - - public static void setLastTX(String wallet, String tx) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - if (!cards.contains(wallet)) { - cards.add(wallet); - editor.putStringSet("LastSign_Cards", cards); - } - - editor.putString("LastSignTX_" + wallet, tx); - editor.putBoolean("LastSignTXComplete_" + wallet, false); - editor.apply(); - } - - public static void setLastMessage(String wallet, String message) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - if (!cards.contains(wallet)) { - cards.add(wallet); - editor.putStringSet("LastSign_Cards", cards); - } - - editor.putString("LastSignMessage_" + wallet, message); - editor.apply(); - } - - public static String getLastMessage(String wallet) - { - try { - String msg = sharedPreferences.getString("LastSignMessage_" + wallet, ""); - return msg; - } - catch(Exception e) - { - return ""; - } - } - - public static void setTxWasSend(String wallet) - { - - Map txList = GetTxList(); - if(txList.containsKey(wallet)) - { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean("LastSignTXComplete_" + wallet, true); - editor.apply(); - } - } - public static boolean getNeedTxSend(String wallet) - { - Map txList = GetTxList(); - if(txList.containsKey(wallet)) - { - boolean complete = txList.get(wallet).isComplete; - return !complete; - } - return false; - } - - public static String getTxForSend(String wallet) - { - Map txList = GetTxList(); - if(txList.containsKey(wallet)) - { - return txList.get(wallet).TX; - } - return ""; - } -} +package com.tangem.wallet; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.ArrayMap; +import android.util.ArraySet; + +import java.lang.reflect.Array; +import java.util.Date; +import java.util.Map; +import java.util.Set; + +/** + * Created by dvol on 30.10.2017. + */ + +public class LastSignStorage { + + private static SharedPreferences sharedPreferences=null; + + private static Set cards = new ArraySet<>(); + private static Map dates = new ArrayMap<>(); + private static Map txCol = new ArrayMap<>(); + private static Map txCompleteCol = new ArrayMap<>(); + + static void Init(Context context) { + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + cards=sharedPreferences.getStringSet("LastSign_Cards", cards); + for (int i = 0; i < cards.size(); i++) { + String wallet = cards.toArray()[i].toString(); + Date dt = new Date(); + dt.setTime(sharedPreferences.getLong("LastSign_" + wallet, 0)); + dates.put(wallet, dt); + } + } + + public static boolean needInit() { + return sharedPreferences==null; + } + + static class CompleteTx + { + public String TX; + public boolean isComplete; + } + public static Map GetTxList() + { + Set wallets=sharedPreferences.getStringSet("LastSign_Cards", cards); + Map txList = new ArrayMap<>(); + + for (int i = 0; i < wallets.size(); i++) { + String wallet = wallets.toArray()[i].toString(); + String tx = sharedPreferences.getString("LastSignTX_" + wallet, ""); + boolean complete = sharedPreferences.getBoolean("LastSignTXComplete_" + wallet, false); + CompleteTx txComplete = new CompleteTx(); + txComplete.isComplete = complete; + txComplete.TX = tx; + txList.put(wallet, txComplete); + } + + return txList; + } + public static Date getLastSignDate(String wallet) { + if (dates.containsKey(wallet)) return dates.get(wallet); + return null; + } + + public static void setLastSignDate(String wallet, Date date) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + if (!cards.contains(wallet)) { + cards.add(wallet); + editor.putStringSet("LastSign_Cards", cards); + } + dates.put(wallet, date); + editor.putLong("LastSign_" + wallet, date.getTime()); + editor.apply(); + } + + public static void setLastTX(String wallet, String tx) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + if (!cards.contains(wallet)) { + cards.add(wallet); + editor.putStringSet("LastSign_Cards", cards); + } + + editor.putString("LastSignTX_" + wallet, tx); + editor.putBoolean("LastSignTXComplete_" + wallet, false); + editor.apply(); + } + + public static void setLastMessage(String wallet, String message) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + if (!cards.contains(wallet)) { + cards.add(wallet); + editor.putStringSet("LastSign_Cards", cards); + } + + editor.putString("LastSignMessage_" + wallet, message); + editor.apply(); + } + + public static String getLastMessage(String wallet) + { + try { + String msg = sharedPreferences.getString("LastSignMessage_" + wallet, ""); + return msg; + } + catch(Exception e) + { + return ""; + } + } + + public static void setTxWasSend(String wallet) + { + + Map txList = GetTxList(); + if(txList.containsKey(wallet)) + { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean("LastSignTXComplete_" + wallet, true); + editor.apply(); + } + } + public static boolean getNeedTxSend(String wallet) + { + Map txList = GetTxList(); + if(txList.containsKey(wallet)) + { + boolean complete = txList.get(wallet).isComplete; + return !complete; + } + return false; + } + + public static String getTxForSend(String wallet) + { + Map txList = GetTxList(); + if(txList.containsKey(wallet)) + { + return txList.get(wallet).TX; + } + return ""; + } +} diff --git a/app/src/main/java/com/tangem/wallet/LoadedWalletActivity.java b/app/src/main/java/com/tangem/wallet/LoadedWalletActivity.java index 9408cb900e..3021483689 100644 --- a/app/src/main/java/com/tangem/wallet/LoadedWalletActivity.java +++ b/app/src/main/java/com/tangem/wallet/LoadedWalletActivity.java @@ -1,37 +1,37 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; - - -public class LoadedWalletActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_loaded_wallet); - MainActivity.commonInit(getApplicationContext()); - - if( getIntent().getExtras().containsKey(NfcAdapter.EXTRA_TAG) ) - { - Tag tag=getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag != null ) { - LoadedWalletActivityFragment fragment=(LoadedWalletActivityFragment)(getSupportFragmentManager().findFragmentById(R.id.loaded_wallet_fragment)); - fragment.onTagDiscovered(tag); - } - } - } - - @Override - public void onBackPressed() { - LoadedWalletActivityFragment loadedWalletActivityFragment=(LoadedWalletActivityFragment) getSupportFragmentManager().findFragmentById(R.id.loaded_wallet_fragment); - Intent data= loadedWalletActivityFragment.prepareResultIntent(); - data.putExtra("modification", "update"); - setResult(Activity.RESULT_OK, data); - finish(); - } -} +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + + +public class LoadedWalletActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_loaded_wallet); + MainActivity.commonInit(getApplicationContext()); + + if( getIntent().getExtras().containsKey(NfcAdapter.EXTRA_TAG) ) + { + Tag tag=getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag != null ) { + LoadedWalletActivityFragment fragment=(LoadedWalletActivityFragment)(getSupportFragmentManager().findFragmentById(R.id.loaded_wallet_fragment)); + fragment.onTagDiscovered(tag); + } + } + } + + @Override + public void onBackPressed() { + LoadedWalletActivityFragment loadedWalletActivityFragment=(LoadedWalletActivityFragment) getSupportFragmentManager().findFragmentById(R.id.loaded_wallet_fragment); + Intent data= loadedWalletActivityFragment.prepareResultIntent(); + data.putExtra("modification", "update"); + setResult(Activity.RESULT_OK, data); + finish(); + } +} diff --git a/app/src/main/java/com/tangem/wallet/LoadedWalletActivityFragment.java b/app/src/main/java/com/tangem/wallet/LoadedWalletActivityFragment.java index e001ac92af..67103317c6 100644 --- a/app/src/main/java/com/tangem/wallet/LoadedWalletActivityFragment.java +++ b/app/src/main/java/com/tangem/wallet/LoadedWalletActivityFragment.java @@ -1,1599 +1,1599 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.ColorStateList; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.IsoDep; -import android.os.AsyncTask; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.widget.SwipeRefreshLayout; -import android.text.Html; -import android.text.Spanned; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.PopupMenu; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.EncodeHintType; -import com.google.zxing.WriterException; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.qrcode.QRCodeWriter; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.NfcManager; -import com.tangem.cardReader.Util; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; - -import static android.content.Context.CLIPBOARD_SERVICE; - -/** - * A placeholder fragment containing a simple view. - */ -public class LoadedWalletActivityFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener, NfcAdapter.ReaderCallback, CardProtocol.Notifications { - - - private static final int REQUEST_CODE_SEND_PAYMENT = 1; - private static final int REQUEST_CODE_PURGE = 2; - private static final int REQUEST_CODE_REQUEST_PIN2_FOR_PURGE = 3; - private static final int REQUEST_CODE_VERIFY_CARD = 4; - private static final int REQUEST_CODE_ENTER_NEW_PIN = 5; - private static final int REQUEST_CODE_ENTER_NEW_PIN2 = 6; - private static final int REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN = 7; - private static final int REQUEST_CODE_SWAP_PIN = 8; - Tangem_Card mCard; - TextView tvCardID, tvBalance, tvOffline, tvBalanceEquivalent, tvWallet, tvInputs, lbInputs, tvOutputs, tvSend, tvPurge, tvError, tvMessage, tvIssuer, tvIssuerData, tvBlockchain, tvLastInput, tvLastOutput, lbLastOutput, tvValidationNode; - TextView tvHeader, tvCaution; - ImageView imgLookup; - TextView tvLookup; - ProgressBar progressBar; - ImageView ivBlockchain, ivPIN, ivPIN2orSecurityDelay, ivDeveloperVersion, ivQR; - SwipeRefreshLayout mSwipeRefreshLayout; - List updateTasks = new ArrayList<>(); - private NfcManager mNfcManager; - private final String logTag = "LoadedWalletFragment"; - private boolean lastReadSuccess = true; - private VerifyCardTask verifyCardTask = null; - private int requestPIN2Count = 0; - - public LoadedWalletActivityFragment() { - - } - - public void onRefresh() { - //Update - // Showing refresh animation before making http call - if (updateTasks.size() > 0) return; - ; - mSwipeRefreshLayout.setRefreshing(true); - mCard.clearInfo(); - mCard.setError(null); - mCard.setMessage(null); - - boolean needResendTX = LastSignStorage.getNeedTxSend(mCard.getWallet()); - - UpdateViews(); - - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - - if (mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet) { - SharedData data = new SharedData(SharedData.COUNT_REQUEST); - for (int i = 0; i < data.allRequest; ++i) { - String nodeAddress = engine.GetNextNode(mCard); - int nodePort = engine.GetNextNodePort(mCard); - UpdateWalletInfoTask connectTaskEx = new UpdateWalletInfoTask(nodeAddress, nodePort, data); - connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(mCard.getWallet())); - } - - String nodeAddress = engine.GetNode(mCard); - int nodePort = engine.GetNodePort(mCard); - UpdateWalletInfoTask updateWalletInfoTask = new UpdateWalletInfoTask(nodeAddress, nodePort, data); - updateTasks.add(updateWalletInfoTask); - updateWalletInfoTask.execute(Electrum_Request.ListUnspent(mCard.getWallet()), Electrum_Request.ListHistory(mCard.getWallet())); - - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(mCard.getWallet(), "bitcoin", "bitcoin"); - taskRate.execute(rate); - - - } else if (mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet) { - SharedData data = new SharedData(SharedData.COUNT_REQUEST); - for (int i = 0; i < data.allRequest; ++i) { - String nodeAddress = engine.GetNextNode(mCard); - int nodePort = engine.GetNextNodePort(mCard); - UpdateWalletInfoTask connectTaskEx = new UpdateWalletInfoTask(nodeAddress, nodePort, data); - connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(mCard.getWallet())); - } - - String nodeAddress = engine.GetNode(mCard); - int nodePort = engine.GetNodePort(mCard); - UpdateWalletInfoTask updateWalletInfoTask = new UpdateWalletInfoTask(nodeAddress, nodePort, data); - - updateTasks.add(updateWalletInfoTask); - updateWalletInfoTask.execute(Electrum_Request.ListUnspent(mCard.getWallet()), Electrum_Request.ListHistory(mCard.getWallet())); - - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(mCard.getWallet(), "bitcoin-cash", "bitcoin-cash"); - taskRate.execute(rate); - - - } - else if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet) { - ETHRequestTask updateETH = new ETHRequestTask(mCard.getBlockchain()); - Infura_Request reqETH = Infura_Request.GetBalance(mCard.getWallet()); - reqETH.setID(67); - reqETH.setBlockchain(mCard.getBlockchain()); - - Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(mCard.getWallet()); - reqNonce.setID(67); - reqNonce.setBlockchain(mCard.getBlockchain()); - - updateETH.execute(reqETH, reqNonce); - - - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(mCard.getWallet(), "ethereum", "ethereum"); - taskRate.execute(rate); - - } else if (mCard.getBlockchain() == Blockchain.Token) { - ETHRequestTask updateETH = new ETHRequestTask(mCard.getBlockchain()); - Infura_Request reqETH = Infura_Request.GetTokenBalance(mCard.getWallet(), engine.GetContractAddress(mCard), engine.GetTokenDecimals(mCard)); - reqETH.setID(67); - reqETH.setBlockchain(mCard.getBlockchain()); - - Infura_Request reqBalance = Infura_Request.GetBalance(mCard.getWallet()); - reqBalance.setID(67); - reqBalance.setBlockchain(mCard.getBlockchain()); - - Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(mCard.getWallet()); - reqNonce.setID(67); - reqNonce.setBlockchain(mCard.getBlockchain()); - updateETH.execute(reqETH, reqNonce, reqBalance); - - - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(mCard.getWallet(), "basic-attention-token", "ethereum"); - taskRate.execute(rate); - } - - if (needResendTX) { - SendTransaction(LastSignStorage.getTxForSend(mCard.getWallet())); - } - } - - void doShareWallet(boolean useURI) { - if (useURI) { - String txtShare = CoinEngineFactory.Create(mCard.getBlockchain()).getShareWalletURI(mCard).toString(); - //String txtShare = Blockchain.getShareWalletURI(mCard).toString(); - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_SUBJECT, "Wallet address"); - intent.putExtra(Intent.EXTRA_TEXT, txtShare); - - PackageManager packageManager = getActivity().getPackageManager(); - List activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL); - boolean isIntentSafe = activities.size() > 0; - - if (isIntentSafe) { - String title = "Share wallet address with:"; - // Create intent to show chooser - Intent chooser = Intent.createChooser(intent, title); - // Verify the intent will resolve to at least one activity - if (intent.resolveActivity(getActivity().getPackageManager()) != null) { - startActivity(chooser); - } - } else { - ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(ClipData.newPlainText(txtShare, txtShare)); - Toast.makeText(getContext(), "Copied to clipboard", Toast.LENGTH_LONG).show(); - } - } else { - String txtShare = mCard.getWallet(); - ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(ClipData.newPlainText(txtShare, txtShare)); - Toast.makeText(getContext(), "Copied to clipboard", Toast.LENGTH_LONG).show(); - } - - - } - - @Override - public View onCreateView(final LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_loaded_wallet, container, false); - - mNfcManager = new NfcManager(this.getActivity(), this); - - - // SwipeRefreshLayout - mSwipeRefreshLayout = v.findViewById(R.id.swipe_container); - mSwipeRefreshLayout.setOnRefreshListener(this); - - mCard = new Tangem_Card(getActivity().getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(getActivity().getIntent().getExtras().getBundle("Card")); - tvCardID = v.findViewById(R.id.tvCardID); - - tvBalance = v.findViewById(R.id.tvBalance); - tvOffline = v.findViewById(R.id.tvOffline); - - if (mCard.getBlockchain() == Blockchain.Token) { - //tvBalance.setLines(2); - tvBalance.setSingleLine(false); - } - - tvBalanceEquivalent = v.findViewById(R.id.tvBalanceEquivalent); - - tvWallet = v.findViewById(R.id.tvWallet); - tvWallet.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - doShareWallet(false); - } - }); - - tvInputs = v.findViewById(R.id.tvInputs); - lbInputs = v.findViewById(R.id.lbInputs); - - tvLastOutput = v.findViewById(R.id.tvLastOutput); - lbLastOutput = v.findViewById(R.id.lbLastOutput); - - final CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - - boolean visibleFlag = engine != null ? engine.InOutPutVisible() : true; - int visibleIOPuts = visibleFlag ? View.VISIBLE : View.GONE; - if (tvInputs != null) { - tvInputs.setVisibility(visibleIOPuts); - } - - if (lbInputs != null) { - lbInputs.setVisibility(visibleIOPuts); - } - - if (tvLastOutput != null) { - tvLastOutput.setVisibility(visibleIOPuts); - } - - if (lbLastOutput != null) { - lbLastOutput.setVisibility(visibleIOPuts); - } - - tvValidationNode = v.findViewById(R.id.tvValidationNode); - - progressBar = v.findViewById(R.id.progressBar); - - tvBlockchain = v.findViewById(R.id.tvBlockchain); - ivBlockchain = v.findViewById(R.id.imgBlockchain); - ivPIN = v.findViewById(R.id.imgPIN); - ivPIN2orSecurityDelay = v.findViewById(R.id.imgPIN2orSecurityDelay); - ivDeveloperVersion = v.findViewById(R.id.imgDeveloperVersion); - - ivQR = v.findViewById(R.id.qrWallet); - - - try { - ivQR.setImageBitmap(generateQrCode(engine.getShareWalletURI(mCard).toString())); - } catch (WriterException e) { - e.printStackTrace(); - } - ivQR.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - doShareWallet(true); - } - }); - - tvError = v.findViewById(R.id.tvError); - tvMessage = v.findViewById(R.id.tvMessage); - - tvIssuer = v.findViewById(R.id.tvIssuer); - tvIssuerData = v.findViewById(R.id.tvIssuerData); - - tvHeader = v.findViewById(R.id.tvHeader); - tvCaution = v.findViewById(R.id.tvCaution); - - tvSend = v.findViewById(R.id.tvSend); - - imgLookup = v.findViewById(R.id.imgLookup); - - if (imgLookup != null) { - - imgLookup.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!mCard.hasBalanceInfo()) { - return; - } - CoinEngine engineClick = CoinEngineFactory.Create(mCard.getBlockchain()); - - Intent browserIntent = new Intent(Intent.ACTION_VIEW, engineClick.getShareWalletURIExplorer(mCard)); - startActivity(browserIntent); - } - }); - - } - - tvLookup = v.findViewById(R.id.tvLookup); - - if (tvLookup != null) { - - tvLookup.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!mCard.hasBalanceInfo()) { - return; - } - CoinEngine engineClick = CoinEngineFactory.Create(mCard.getBlockchain()); - - Intent browserIntent = new Intent(Intent.ACTION_VIEW, engineClick.getShareWalletURIExplorer(mCard)); - startActivity(browserIntent); - } - }); - } - - if (tvSend != null) { - tvSend.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!mCard.hasBalanceInfo()) { - return; - } else if (!engine.IsBalanceNotZero(mCard)) { - Toast.makeText(getContext(), "The wallet is empty", Toast.LENGTH_LONG).show(); - return; - } else if (!engine.IsBalanceAlterNotZero(mCard)) { - Toast.makeText(getContext(), "Not enough funds for transaction fee (gas)!", Toast.LENGTH_LONG).show(); - return; - } else if (engine.AwaitingConfirmation(mCard)) { - Toast.makeText(getContext(), "Please wait while previous transaction is confirmed in blockchain", Toast.LENGTH_LONG).show(); - return; - } else if (!engine.CheckUnspentTransaction(mCard)) { - Toast.makeText(getContext(), "Please wait for confirmation of incoming transaction!", Toast.LENGTH_LONG).show(); - return; - } else if (mCard.getRemainingSignatures() == 0) { - Toast.makeText(getContext(), "Card hasn't remaining signature!", Toast.LENGTH_LONG).show(); - return; - } - - Intent intent = new Intent(getContext(), PreparePaymentActivity.class); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_SEND_PAYMENT); - } - }); - } - - tvPurge = v.findViewById(R.id.tvPurge); - if (tvPurge != null) { - tvPurge.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showMenu(tvPurge); - } - } - ); - } - UpdateViews(); - - if (!mCard.hasBalanceInfo()) { - mSwipeRefreshLayout.setRefreshing(true); - mSwipeRefreshLayout.postDelayed(new Runnable() { - @Override - public void run() { - onRefresh(); - } - }, 1000); - } - return v; - } - - public void showMenu(View v) { - final PopupMenu popup = new PopupMenu(getActivity(), v); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.menu_loaded_wallet, popup.getMenu()); - - popup.getMenu().findItem(R.id.action_set_PIN1).setVisible(mCard.allowSwapPIN()); - popup.getMenu().findItem(R.id.action_reset_PIN1).setVisible(mCard.allowSwapPIN() && !mCard.useDefaultPIN1()); - popup.getMenu().findItem(R.id.action_set_PIN2).setVisible(mCard.allowSwapPIN2()); - popup.getMenu().findItem(R.id.action_reset_PIN2).setVisible(mCard.allowSwapPIN2() && !mCard.useDefaultPIN2()); - popup.getMenu().findItem(R.id.action_reset_PINs).setVisible(mCard.allowSwapPIN() && mCard.allowSwapPIN2() && !mCard.useDefaultPIN1() && !mCard.useDefaultPIN2()); - - popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - - int id = item.getItemId(); - switch (id) { - case R.id.action_set_PIN1: - doSetPin(); - return true; - case R.id.action_reset_PIN1: - doResetPin(); - return true; - case R.id.action_set_PIN2: - doSetPin2(); - return true; - case R.id.action_reset_PIN2: - doResetPin2(); - return true; - case R.id.action_reset_PINs: - doResetPins(); - return true; - case R.id.action_purge: - doPurge(); - return true; - default: - return false; - } - } - }); - popup.show(); - } - - String newPIN = "", newPIN2 = ""; - - void doSetPin() { - requestPIN2Count = 0; - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestNewPIN.toString()); - newPIN = ""; - newPIN2 = ""; - startActivityForResult(intent, REQUEST_CODE_ENTER_NEW_PIN); - } - - void doResetPin() { - requestPIN2Count = 0; - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - newPIN = PINStorage.getDefaultPIN(); - newPIN2 = ""; - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); - } - - void doResetPin2() { - requestPIN2Count = 0; - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - newPIN = ""; - newPIN2 = PINStorage.getDefaultPIN2(); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); - } - - void doResetPins() { - requestPIN2Count = 0; - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - newPIN = PINStorage.getDefaultPIN(); - newPIN2 = PINStorage.getDefaultPIN2(); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); - } - - - void doSetPin2() { - requestPIN2Count = 0; - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestNewPIN2.toString()); - newPIN = ""; - newPIN2 = ""; - startActivityForResult(intent, REQUEST_CODE_ENTER_NEW_PIN2); - } - - void doPurge() { - requestPIN2Count = 0; - final CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - if (!mCard.hasBalanceInfo()) { - return; - } else if (engine.IsBalanceNotZero(mCard)) { - Toast.makeText(getContext(), "Cannot erase wallet with non-zero balance", Toast.LENGTH_LONG).show(); - return; - } - - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_PURGE); - } - - void UpdateViews() { - try { - if (timerHideErrorAndMessage != null) { - timerHideErrorAndMessage.cancel(); - timerHideErrorAndMessage = null; - } - tvCardID.setText(mCard.getCIDDescription()); - - if ((mCard.getError() == null || mCard.getError().isEmpty())) { - tvError.setVisibility(View.GONE); - tvError.setText(""); - } else { - tvError.setVisibility(View.VISIBLE); - tvError.setText(mCard.getError()); - } - - boolean needResendTX = LastSignStorage.getNeedTxSend(mCard.getWallet()); - - if ((mCard.getMessage() == null || mCard.getMessage().isEmpty()) && !needResendTX) { - tvMessage.setText(""); - tvMessage.setVisibility(View.GONE); - } else { - if (needResendTX) { - tvMessage.setText("Sending cached transaction..."); - } else { - tvMessage.setText(mCard.getMessage()); - } - tvMessage.setVisibility(View.VISIBLE); - - } - - - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - - if (engine.HasBalanceInfo(mCard) || mCard.getOfflineBalance() == null) { - if (mCard.getBlockchain() == Blockchain.Token) { - Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard)); - tvBalance.setText(html); - } else { - tvBalance.setText(engine.GetBalanceWithAlter(mCard)); - } - - tvBalanceEquivalent.setText(engine.GetBalanceEquivalent(mCard)); - tvOffline.setVisibility(View.INVISIBLE); - } else { - String offlineAmount = engine.ConvertByteArrayToAmount(mCard, mCard.getOfflineBalance()); - if (mCard.getBlockchain() == Blockchain.Token) { - tvBalance.setText("NOT IMPLEMENTED"); - } else { - tvBalance.setText(engine.GetAmountDescription(mCard, offlineAmount)); - } - - tvBalanceEquivalent.setText(engine.GetAmountEqualentDescriptor(mCard, offlineAmount)); - tvOffline.setVisibility(View.VISIBLE); - } - - if (!mCard.getAmountEquivalentDescriptionAvailable()) { - //tvBalanceEquivalent.setError("Service unavailable"); - } else { - tvBalanceEquivalent.setError(null); - } - - tvWallet.setText(mCard.getWallet()); - - tvInputs.setText(mCard.getInputsDescription()); - if (mCard.getLastInputDescription().contains("awaiting")) { - tvInputs.setTextColor(getContext().getResources().getColor(R.color.not_confirmed, getContext().getTheme())); - } else if (mCard.getLastInputDescription().contains("None")) { - tvInputs.setTextColor(getContext().getResources().getColor(R.color.primary_dark, getContext().getTheme())); - } else { - tvInputs.setTextColor(getContext().getResources().getColor(R.color.confirmed, getContext().getTheme())); - } - - if (tvOutputs != null) { - tvOutputs.setText(mCard.getOutputsDescription()); - } - - if (tvLastInput != null) { - tvLastInput.setText(mCard.getLastInputDescription()); - } - if (tvLastOutput != null) { - tvLastOutput.setText(mCard.getLastOutputDescription()); - } - - tvBlockchain.setText(mCard.getBlockchainName()); - ivBlockchain.setImageResource(mCard.getBlockchain().getImageResource(this.getContext(), mCard.getTokenSymbol())); - - if (tvValidationNode != null) { - tvValidationNode.setText(mCard.getValidationNodeDescription()); - } - - if (mCard.useDefaultPIN1()) { - ivPIN.setImageResource(R.drawable.unlock_pin1); - ivPIN.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "This banknote is protected by default PIN1 code", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivPIN.setImageResource(R.drawable.lock_pin1); - ivPIN.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "This banknote is protected by user's PIN1 code", Toast.LENGTH_LONG).show(); - } - }); - } - - if (mCard.getPauseBeforePIN2() > 0 && (mCard.useDefaultPIN2() || !mCard.useSmartSecurityDelay())) { - ivPIN2orSecurityDelay.setImageResource(R.drawable.timer); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), String.format("This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code", mCard.getPauseBeforePIN2() / 1000.0), Toast.LENGTH_LONG).show(); - } - }); - - } else if (mCard.useDefaultPIN2()) { - ivPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "This banknote is protected by default PIN2 code", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "This banknote is protected by user's PIN2 code", Toast.LENGTH_LONG).show(); - } - }); - } - - - if (mCard.useDevelopersFirmware()) { - ivDeveloperVersion.setImageResource(R.drawable.ic_developer_version); - ivDeveloperVersion.setVisibility(View.VISIBLE); - ivDeveloperVersion.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "Unlocked banknote, only for development use", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivDeveloperVersion.setVisibility(View.INVISIBLE); - } - - if (tvSend != null) { - if (mCard.hasBalanceInfo()) { - tvSend.setEnabled(true); - } else { - tvSend.setEnabled(false); - } - } - - if (tvPurge != null) { - if (mCard.hasBalanceInfo()) { - tvPurge.setEnabled(true); - } else { - tvPurge.setEnabled(false); - } - } - - tvIssuer.setText(mCard.getIssuerDescription()); - - timerHideErrorAndMessage = new Timer(); - - - timerHideErrorAndMessage.schedule(new TimerTask() { - @Override - public void run() { - tvError.post(new Runnable() { - @Override - public void run() { - tvMessage.setVisibility(View.GONE); - tvError.setVisibility(View.GONE); - mCard.setError(null); - mCard.setMessage(null); - } - }); - } - }, 5000); - - if (mCard.isReusable()) { - tvHeader.setText("REUSABLE WALLET"); - tvCaution.setVisibility(View.GONE); - } else { - if (mCard.getMaxSignatures() == mCard.getRemainingSignatures()) { - tvHeader.setText("BANKNOTE"); - tvCaution.setVisibility(View.GONE); - } else { - tvHeader.setText("NON-TRANSFERABLE BANKNOTE"); - tvCaution.setVisibility(View.VISIBLE); - } - } - - if (mCard.useDevelopersFirmware()) { - tvHeader.setText("DEVELOPER KIT"); - tvCaution.setVisibility(View.VISIBLE); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - Timer timerHideErrorAndMessage = null; - - public Intent prepareResultIntent() { - Intent data = new Intent(); - data.putExtra("UID", mCard.getUID()); - data.putExtra("Card", mCard.getAsBundle()); - return data; - } - - private void SendTransaction(String tx) { - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet || mCard.getBlockchain() == Blockchain.Token) { - ETHRequestTask task = new ETHRequestTask(mCard.getBlockchain()); - Infura_Request req = Infura_Request.SendTransaction(mCard.getWallet(), tx); - req.setID(67); - req.setBlockchain(mCard.getBlockchain()); - task.execute(req); - } else if (mCard.getBlockchain() == Blockchain.Bitcoin ||mCard.getBlockchain() == Blockchain.BitcoinTestNet) { - String nodeAddress = engine.GetNode(mCard); - int nodePort = engine.GetNodePort(mCard); - - UpdateWalletInfoTask connectTask = new UpdateWalletInfoTask(nodeAddress, nodePort); - connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx)); - } - else if (mCard.getBlockchain() == Blockchain.BitcoinCash ||mCard.getBlockchain() == Blockchain.BitcoinCashTestNet) { - String nodeAddress = engine.GetNode(mCard); - int nodePort = engine.GetNodePort(mCard); - - UpdateWalletInfoTask connectTask = new UpdateWalletInfoTask(nodeAddress, nodePort); - connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx)); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case REQUEST_CODE_ENTER_NEW_PIN: - if (resultCode == Activity.RESULT_OK) { - if (data != null) { - if (data.getExtras().containsKey("confirmPIN")) { - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - newPIN = data.getStringExtra("newPIN"); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); - } else { - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("newPIN", data.getStringExtra("newPIN")); - intent.putExtra("mode", RequestPINActivity.Mode.ConfirmNewPIN.toString()); - startActivityForResult(intent, REQUEST_CODE_ENTER_NEW_PIN); - } - } - } - break; - case REQUEST_CODE_ENTER_NEW_PIN2: - if (resultCode == Activity.RESULT_OK) { - if (data != null) { - if (data.getExtras().containsKey("confirmPIN2")) { - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - newPIN2 = data.getStringExtra("newPIN2"); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); - } else { - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("newPIN2", data.getStringExtra("newPIN2")); - intent.putExtra("mode", RequestPINActivity.Mode.ConfirmNewPIN2.toString()); - startActivityForResult(intent, REQUEST_CODE_ENTER_NEW_PIN2); - } - } - } - break; - case REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN: - if (resultCode == Activity.RESULT_OK) { - if (newPIN.equals("")) { - newPIN = mCard.getPIN(); - } - if (newPIN2.equals("")) { - newPIN2 = PINStorage.getPIN2(); - } - - PINSwapWarningDialog dialog = (new PINSwapWarningDialog()); - dialog.activityFragment = this; - if (!PINStorage.isDefaultPIN(newPIN) || !PINStorage.isDefaultPIN2(newPIN2)) { - dialog.message = "If you forget your new PIN you will lose your money forever!"; - } else { - dialog.message = "If you use default PIN someone can steal your money!"; - } - dialog.show(getActivity().getFragmentManager(), "PINSwapWarningDialog"); - } - break; - - case REQUEST_CODE_SWAP_PIN: - if (resultCode == Activity.RESULT_OK) { - if (data == null) { - data = new Intent(); - - data.putExtra("UID", mCard.getUID()); - data.putExtra("Card", mCard.getAsBundle()); - data.putExtra("modification", "delete"); - } else { - data.putExtra("modification", "update"); - } - getActivity().setResult(Activity.RESULT_OK, data); - getActivity().finish(); - } else { - if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { - Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID")); - updatedCard.LoadFromBundle(data.getBundleExtra("Card")); - mCard = updatedCard; - } - if (resultCode == CreateNewWalletActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) { - requestPIN2Count++; - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); - return; - } else { - if (data != null && data.getExtras().containsKey("message")) { - mCard.setError(data.getStringExtra("message")); - } - } - } - break; - case REQUEST_CODE_REQUEST_PIN2_FOR_PURGE: - if (resultCode == Activity.RESULT_OK) { - Intent intent = new Intent(getContext(), PurgeActivity.class); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_PURGE); - } - break; - case REQUEST_CODE_PURGE: - if (resultCode == Activity.RESULT_OK) { - if (data == null) { - data = new Intent(); - - data.putExtra("UID", mCard.getUID()); - data.putExtra("Card", mCard.getAsBundle()); - data.putExtra("modification", "delete"); - } else { - data.putExtra("modification", "update"); - } - getActivity().setResult(Activity.RESULT_OK, data); - getActivity().finish(); - } else { - if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { - Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID")); - updatedCard.LoadFromBundle(data.getBundleExtra("Card")); - mCard = updatedCard; - } - if (resultCode == CreateNewWalletActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) { - requestPIN2Count++; - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_PURGE); - return; - } else { - if (data != null && data.getExtras().containsKey("message")) { - mCard.setError(data.getStringExtra("message")); - } - } - UpdateViews(); - } - break; - case REQUEST_CODE_SEND_PAYMENT: - if (resultCode == Activity.RESULT_OK) { - mSwipeRefreshLayout.postDelayed(new Runnable() { - @Override - public void run() { - onRefresh(); - } - }, 10000); - mSwipeRefreshLayout.setRefreshing(true); - mCard.clearInfo(); - UpdateViews(); - } - - if (data != null) { - if (data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { - Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID")); - updatedCard.LoadFromBundle(data.getBundleExtra("Card")); - mCard = updatedCard; - } - if (data.getExtras().containsKey("message")) { - if (resultCode == Activity.RESULT_OK) { - mCard.setMessage(data.getStringExtra("message")); - } else { - mCard.setError(data.getStringExtra("message")); - } - } - UpdateViews(); - } - - break; - } - - } - - private void startSwapPINActivity() { - Intent intent = new Intent(getContext(), SwapPINActivity.class); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - intent.putExtra("newPIN", newPIN); - intent.putExtra("newPIN2", newPIN2); - startActivityForResult(intent, REQUEST_CODE_SWAP_PIN); - } - - public static Bitmap generateQrCode(String myCodeText) throws WriterException { - Hashtable hintMap = new Hashtable(); - hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // H = 30% damage - - QRCodeWriter qrCodeWriter = new QRCodeWriter(); - - int size = 256; - - BitMatrix bitMatrix = qrCodeWriter.encode(myCodeText, BarcodeFormat.QR_CODE, size, size, hintMap); - int width = bitMatrix.getWidth(); - Bitmap bmp = Bitmap.createBitmap(width, width, Bitmap.Config.RGB_565); - for (int x = 0; x < width; x++) { - for (int y = 0; y < width; y++) { - bmp.setPixel(y, x, bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE); - } - } - return bmp; - } - - - @Override - public void onResume() { - super.onResume(); - mNfcManager.onResume(); - } - - @Override - public void onPause() { - super.onPause(); - mNfcManager.onPause(); - } - - @Override - public void onStop() { - super.onStop(); - for (UpdateWalletInfoTask ut : updateTasks) { - ut.cancel(true); - } - mNfcManager.onStop(); - } - - @Override - public void onTagDiscovered(Tag tag) { - try { - final IsoDep isoDep = IsoDep.get(tag); - if (isoDep == null) { - throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); - } - byte UID[] = tag.getId(); - String sUID = Util.byteArrayToHexString(UID); - if (!mCard.getUID().equals(sUID)) { - Log.d(logTag, "Invalid UID: " + sUID); - mNfcManager.IgnoreTag(isoDep.getTag()); - return; - } else { - Log.v(logTag, "UID: " + sUID); - } - - if (lastReadSuccess) { - isoDep.setTimeout(1000); - } else { - isoDep.setTimeout(65000); - } - //lastTag = tag; - verifyCardTask = new VerifyCardTask(getContext(), mCard, mNfcManager, isoDep, this); - verifyCardTask.start(); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - public void ErrorOnUpdate(String message) { - mCard.setError("Cannot obtain data from blockchain"); - UpdateViews(); - } - - private class ETHRequestTask extends Infura_Task { - ETHRequestTask(Blockchain blockchain) { - super(blockchain); - } - - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - for (Infura_Request request : requests) { - try { - if (request.error == null) { - - if (request.isMethod(Infura_Request.METHOD_ETH_GetBalance)) { - try { - String balanceCap = request.getResultString(); - balanceCap = balanceCap.substring(2); - BigInteger l = new BigInteger(balanceCap, 16); - BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); - Long balance = d.longValue(); - - mCard.setBalanceConfirmed(balance); - mCard.setBalanceUnconfirmed(0L); - if (mCard.getBlockchain() != Blockchain.Token) - mCard.setDecimalBalance(l.toString(10)); - mCard.setDecimalBalanceAlter(l.toString(10)); - - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - } - } else if (request.isMethod(Infura_Request.METHOD_ETH_Call)) { - try { - String balanceCap = request.getResultString(); - balanceCap = balanceCap.substring(2); - BigInteger l = new BigInteger(balanceCap, 16); - Long balance = l.longValue(); - - if (l.compareTo(BigInteger.ZERO) == 0) { - mCard.setBlockchainID(Blockchain.Ethereum.getID()); - mCard.addTokenToBlockchainName(); - mSwipeRefreshLayout.setRefreshing(false); - onRefresh(); - return; - } - - mCard.setBalanceConfirmed(balance); - mCard.setBalanceUnconfirmed(0L); - mCard.setDecimalBalance(l.toString(10)); - - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - } - } else if (request.isMethod(Infura_Request.METHOD_ETH_GetOutTransactionCount)) { - try { - String nonce = request.getResultString(); - nonce = nonce.substring(2); - BigInteger count = new BigInteger(nonce, 16); - - mCard.SetConfirmTXCount(count); - } catch (JSONException e) { - e.printStackTrace(); - } - } else if (request.isMethod(Infura_Request.METHOD_ETH_SendRawTransaction)) { - try { - String hashTX = ""; - - try { - String tmp = request.getResultString(); - hashTX = tmp; - } catch (JSONException e) { - JSONObject msg = request.getAnswer(); - JSONObject err = msg.getJSONObject("error"); - hashTX = err.getString("message"); - LastSignStorage.setLastMessage(mCard.getWallet(), hashTX); - ErrorOnUpdate("Failed to send transaction. Try again"); - return; - } - - if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { - hashTX = hashTX.substring(2); - } - BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ - LastSignStorage.setTxWasSend(mCard.getWallet()); - LastSignStorage.setLastMessage(mCard.getWallet(), ""); - Log.e("TX_RESULT", hashTX); - - - BigInteger nonce = mCard.GetConfirmTXCount(); - nonce.add(BigInteger.valueOf(1)); - mCard.SetConfirmTXCount(nonce); - Log.e("TX_RESULT", hashTX); - - } catch (Exception e) { - e.printStackTrace(); - ErrorOnUpdate("Failed to send transaction. Try again"); - } - } - UpdateViews(); - } else { - ErrorOnUpdate(request.error); - } - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - } - } - - if (updateTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); - } - } - - public void OnReadStart(CardProtocol cardProtocol) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setVisibility(View.VISIBLE); - progressBar.setProgress(5); - } - }); - } - - public void OnReadFinish(final CardProtocol cardProtocol) { - - verifyCardTask = null; - - if (cardProtocol != null) { - if (cardProtocol.getError() == null) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); - Intent intent = new Intent(getContext(), VerifyCardActivity.class); - // TODO обновить карту mCard - intent.putExtra("UID", cardProtocol.getCard().getUID()); - intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); - startActivityForResult(intent, REQUEST_CODE_VERIFY_CARD); - //addCard(cardProtocol.getCard()); - } - }); - } else { - // remove last UIDs because of error and no card read - progressBar.post(new Runnable() { - @Override - public void run() { - lastReadSuccess = false; - if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { - if (!NoExtendedLengthSupportDialog.allreadyShowed) { - new NoExtendedLengthSupportDialog().show(getActivity().getFragmentManager(), "NoExtendedLengthSupportDialog"); - } - } else { - Toast.makeText(getContext(), "Try to scan again", Toast.LENGTH_LONG).show(); - } - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - } - }); - } - } - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - public void OnReadProgress(CardProtocol protocol, final int progress) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(progress); - } - }); - } - - public void OnReadCancel() { - - verifyCardTask = null; - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - public void OnReadWait(int msec) { - WaitSecurityDelayDialog.OnReadWait(getActivity(), msec); - } - - @Override - public void OnReadBeforeRequest(int timeout) { - WaitSecurityDelayDialog.onReadBeforeRequest(getActivity(), timeout); - } - - @Override - public void OnReadAfterRequest() { - WaitSecurityDelayDialog.onReadAfterRequest(getActivity()); - } - - - private class RateInfoTask extends ExchangeTask { - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - for (ExchangeRequest request : requests) { - if (request.error == null) { - try { - - JSONArray arr = request.getAnswerList(); - for (int i = 0; i < arr.length(); ++i) { - JSONObject obj = arr.getJSONObject(i); - String currency = obj.getString("id"); - - boolean stop = false; - boolean stopAlter = false; - if (currency.equals(request.currency)) { - String usd = obj.getString("price_usd"); - - Float rate = Float.valueOf(usd); - mCard.setRate(rate); - UpdateViews(); - stop = true; - } - - if (currency.equals(request.currencyAlter)) { - String usd = obj.getString("price_usd"); - - Float rate = Float.valueOf(usd); - mCard.setRateAlter(rate); - UpdateViews(); - stopAlter = true; - } - - if (stop && stopAlter) { - break; - } - - } - } catch (JSONException e) { - e.printStackTrace(); - } - } - } - } - } - - private class UpdateWalletInfoTask extends Electrum_Task { - public UpdateWalletInfoTask(String host, int port) { - super(host, port); - } - - - public UpdateWalletInfoTask(String host, int port, SharedData sharedData) { - super(host, port, sharedData); - } - - @Override - protected void onProgressUpdate(Integer... values) { - super.onProgressUpdate(values); - } - - @Override - protected void onCancelled() { - super.onCancelled(); - updateTasks.remove(this); - if (updateTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); - } - - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - Log.i("RequestWalletInfoTask", "onPostExecute[" + String.valueOf(updateTasks.size()) + "]"); - updateTasks.remove(this); - - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - - for (Electrum_Request request : requests) { - try { - if (request.error == null) { - if (request.isMethod(Electrum_Request.METHOD_GetBalance)) { - try { - String mWalletAddress = request.getParams().getString(0); - Long confBalance = request.getResult().getLong("confirmed"); - Long unconf = request.getResult().getLong("unconfirmed"); - if (sharedCounter != null) { - int counter = sharedCounter.requestCounter.incrementAndGet(); - if (counter != 1) { - continue; - } - } - - mCard.setBalanceConfirmed(confBalance); - mCard.setBalanceUnconfirmed(unconf); - mCard.setDecimalBalance(String.valueOf(confBalance)); - mCard.setValidationNodeDescription(getValidationNodeDescription()); - } catch (JSONException e) { - if (sharedCounter != null) { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if (errCounter >= sharedCounter.allRequest) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - } - } else { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - } - } - } else if (request.isMethod(Electrum_Request.METHOD_SendTransaction)) { - try { - String hashTX = request.getResultString(); - - try { - LastSignStorage.setLastMessage(mCard.getWallet(), hashTX); - if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { - hashTX = hashTX.substring(2); - } - BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ - LastSignStorage.setTxWasSend(mCard.getWallet()); - LastSignStorage.setLastMessage(mCard.getWallet(), ""); - Log.e("TX_RESULT", hashTX); - - } catch (Exception e) { - engine.SwitchNode(mCard); - ErrorOnUpdate("Failed to send transaction. Try again."); - } - - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate("Failed to send transaction. Try again."); - engine.SwitchNode(mCard); - } - } else if (request.isMethod(Electrum_Request.METHOD_ListUnspent)) { - try { - String mWalletAddress = request.getParams().getString(0); - - JSONArray jsUnspentArray = request.getResultArray(); - try { - mCard.getUnspentTransactions().clear(); - for (int i = 0; i < jsUnspentArray.length(); i++) { - JSONObject jsUnspent = jsUnspentArray.getJSONObject(i); - Tangem_Card.UnspentTransaction trUnspent = new Tangem_Card.UnspentTransaction(); - trUnspent.txID = jsUnspent.getString("tx_hash"); - trUnspent.Amount = jsUnspent.getInt("value"); - trUnspent.Height = jsUnspent.getInt("height"); - mCard.getUnspentTransactions().add(trUnspent); - } - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - } - - for (int i = 0; i < jsUnspentArray.length(); i++) { - JSONObject jsUnspent = jsUnspentArray.getJSONObject(i); - Integer height = jsUnspent.getInt("height"); - String hash = jsUnspent.getString("tx_hash"); - if (height != -1) { - String nodeAddress = engine.GetNextNode(mCard); - int nodePort = engine.GetNextNodePort(mCard); - UpdateWalletInfoTask updateWalletInfoTask = new UpdateWalletInfoTask(nodeAddress, nodePort); - - updateTasks.add(updateWalletInfoTask); - - updateWalletInfoTask.execute(Electrum_Request.GetHeader(mWalletAddress, String.valueOf(height)), - Electrum_Request.GetTransaction(mWalletAddress, hash)); - } - } - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - } - } else if (request.isMethod(Electrum_Request.METHOD_GetHistory)) { - try { - String mWalletAddress = request.getParams().getString(0); - - JSONArray jsHistoryArray = request.getResultArray(); - try { - mCard.getHistoryTransactions().clear(); - for (int i = 0; i < jsHistoryArray.length(); i++) { - JSONObject jsUnspent = jsHistoryArray.getJSONObject(i); - Tangem_Card.HistoryTransaction trHistory = new Tangem_Card.HistoryTransaction(); - trHistory.txID = jsUnspent.getString("tx_hash"); - trHistory.Height = jsUnspent.getInt("height"); - mCard.getHistoryTransactions().add(trHistory); - } - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - } - - for (int i = 0; i < jsHistoryArray.length(); i++) { - JSONObject jsUnspent = jsHistoryArray.getJSONObject(i); - Integer height = jsUnspent.getInt("height"); - String hash = jsUnspent.getString("tx_hash"); - if (height != -1) { - - String nodeAddress = engine.GetNode(mCard); - int nodePort = engine.GetNodePort(mCard); - UpdateWalletInfoTask updateWalletInfoTask = new UpdateWalletInfoTask(nodeAddress, nodePort); - updateTasks.add(updateWalletInfoTask); - - updateWalletInfoTask.execute(Electrum_Request.GetHeader(mWalletAddress, String.valueOf(height)), - Electrum_Request.GetTransaction(mWalletAddress, hash)); - } - - } - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - } - } else if (request.isMethod(Electrum_Request.METHOD_GetHeader)) { - try { - JSONObject jsHeader = request.getResult(); - try { - mCard.getHaedersInfo(); - mCard.UpdateHeaderInfo(new Tangem_Card.HeaderInfo( - jsHeader.getInt("block_height"), - jsHeader.getInt("timestamp"))); - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - - } - - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - - } - } else if (request.isMethod(Electrum_Request.METHOD_GetTransaction)) { - try { - - String txHash = request.TxHash; - String raw = request.getResultString(); - - List listTx = mCard.getUnspentTransactions(); - for (Tangem_Card.UnspentTransaction tx : listTx) { - if (tx.txID.equals(txHash)) { - tx.Raw = raw; - } - } - - List listHTx = mCard.getHistoryTransactions(); - for (Tangem_Card.HistoryTransaction tx : listHTx) { - if (tx.txID.equals(txHash)) { - tx.Raw = raw; - try { - ArrayList prevHashes = BTCUtils.getPrevTX(raw); - - boolean isOur = false; - for (byte[] hash : prevHashes) { - String checkID = BTCUtils.toHex(hash); - for (Tangem_Card.HistoryTransaction txForCheck : listHTx) { - if (txForCheck.txID == checkID) { - isOur = true; - } - } - } - - tx.isInput = !isOur; - } catch (BitcoinException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - } - Log.e("TX", raw); - } - } - - } catch (JSONException e) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - engine.SwitchNode(mCard); - } - } - UpdateViews(); - } else { - if (sharedCounter != null) { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if (errCounter >= sharedCounter.allRequest) { - ErrorOnUpdate(request.error); - engine.SwitchNode(mCard); - - } - } else { - ErrorOnUpdate(request.error); - engine.SwitchNode(mCard); - - } - - } - } catch (JSONException e) { - if (sharedCounter != null) { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if (errCounter >= sharedCounter.allRequest) { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - } - } else { - e.printStackTrace(); - ErrorOnUpdate(e.toString()); - } - } - } - if (updateTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); - - } - } - - public static class PINSwapWarningDialog extends DialogFragment { - - LoadedWalletActivityFragment activityFragment = null; - String message; - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - return new AlertDialog.Builder(getActivity()) - .setIcon(R.drawable.tangem_logo_small_new) - .setTitle("Your money is at risk!") - .setMessage(message) - .setCancelable(true) - .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - PINSwapWarningDialog.this.dismiss(); - } - }) - .setPositiveButton("Continue", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - if (activityFragment != null) - activityFragment.startSwapPINActivity(); - } - } - ) - .create(); - } - - @Override - public void onCancel(DialogInterface dialog) { - super.onCancel(dialog); - } - } - -} +package com.tangem.wallet; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.widget.SwipeRefreshLayout; +import android.text.Html; +import android.text.Spanned; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.PopupMenu; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.NfcManager; +import com.tangem.cardReader.Util; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import static android.content.Context.CLIPBOARD_SERVICE; + +/** + * A placeholder fragment containing a simple view. + */ +public class LoadedWalletActivityFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener, NfcAdapter.ReaderCallback, CardProtocol.Notifications { + + + private static final int REQUEST_CODE_SEND_PAYMENT = 1; + private static final int REQUEST_CODE_PURGE = 2; + private static final int REQUEST_CODE_REQUEST_PIN2_FOR_PURGE = 3; + private static final int REQUEST_CODE_VERIFY_CARD = 4; + private static final int REQUEST_CODE_ENTER_NEW_PIN = 5; + private static final int REQUEST_CODE_ENTER_NEW_PIN2 = 6; + private static final int REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN = 7; + private static final int REQUEST_CODE_SWAP_PIN = 8; + Tangem_Card mCard; + TextView tvCardID, tvBalance, tvOffline, tvBalanceEquivalent, tvWallet, tvInputs, lbInputs, tvOutputs, tvSend, tvPurge, tvError, tvMessage, tvIssuer, tvIssuerData, tvBlockchain, tvLastInput, tvLastOutput, lbLastOutput, tvValidationNode; + TextView tvHeader, tvCaution; + ImageView imgLookup; + TextView tvLookup; + ProgressBar progressBar; + ImageView ivBlockchain, ivPIN, ivPIN2orSecurityDelay, ivDeveloperVersion, ivQR; + SwipeRefreshLayout mSwipeRefreshLayout; + List updateTasks = new ArrayList<>(); + private NfcManager mNfcManager; + private final String logTag = "LoadedWalletFragment"; + private boolean lastReadSuccess = true; + private VerifyCardTask verifyCardTask = null; + private int requestPIN2Count = 0; + + public LoadedWalletActivityFragment() { + + } + + public void onRefresh() { + //Update + // Showing refresh animation before making http call + if (updateTasks.size() > 0) return; + ; + mSwipeRefreshLayout.setRefreshing(true); + mCard.clearInfo(); + mCard.setError(null); + mCard.setMessage(null); + + boolean needResendTX = LastSignStorage.getNeedTxSend(mCard.getWallet()); + + UpdateViews(); + + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + + if (mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet) { + SharedData data = new SharedData(SharedData.COUNT_REQUEST); + for (int i = 0; i < data.allRequest; ++i) { + String nodeAddress = engine.GetNextNode(mCard); + int nodePort = engine.GetNextNodePort(mCard); + UpdateWalletInfoTask connectTaskEx = new UpdateWalletInfoTask(nodeAddress, nodePort, data); + connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(mCard.getWallet())); + } + + String nodeAddress = engine.GetNode(mCard); + int nodePort = engine.GetNodePort(mCard); + UpdateWalletInfoTask updateWalletInfoTask = new UpdateWalletInfoTask(nodeAddress, nodePort, data); + updateTasks.add(updateWalletInfoTask); + updateWalletInfoTask.execute(Electrum_Request.ListUnspent(mCard.getWallet()), Electrum_Request.ListHistory(mCard.getWallet())); + + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(mCard.getWallet(), "bitcoin", "bitcoin"); + taskRate.execute(rate); + + + } else if (mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet) { + SharedData data = new SharedData(SharedData.COUNT_REQUEST); + for (int i = 0; i < data.allRequest; ++i) { + String nodeAddress = engine.GetNextNode(mCard); + int nodePort = engine.GetNextNodePort(mCard); + UpdateWalletInfoTask connectTaskEx = new UpdateWalletInfoTask(nodeAddress, nodePort, data); + connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(mCard.getWallet())); + } + + String nodeAddress = engine.GetNode(mCard); + int nodePort = engine.GetNodePort(mCard); + UpdateWalletInfoTask updateWalletInfoTask = new UpdateWalletInfoTask(nodeAddress, nodePort, data); + + updateTasks.add(updateWalletInfoTask); + updateWalletInfoTask.execute(Electrum_Request.ListUnspent(mCard.getWallet()), Electrum_Request.ListHistory(mCard.getWallet())); + + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(mCard.getWallet(), "bitcoin-cash", "bitcoin-cash"); + taskRate.execute(rate); + + + } + else if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet) { + ETHRequestTask updateETH = new ETHRequestTask(mCard.getBlockchain()); + Infura_Request reqETH = Infura_Request.GetBalance(mCard.getWallet()); + reqETH.setID(67); + reqETH.setBlockchain(mCard.getBlockchain()); + + Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(mCard.getWallet()); + reqNonce.setID(67); + reqNonce.setBlockchain(mCard.getBlockchain()); + + updateETH.execute(reqETH, reqNonce); + + + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(mCard.getWallet(), "ethereum", "ethereum"); + taskRate.execute(rate); + + } else if (mCard.getBlockchain() == Blockchain.Token) { + ETHRequestTask updateETH = new ETHRequestTask(mCard.getBlockchain()); + Infura_Request reqETH = Infura_Request.GetTokenBalance(mCard.getWallet(), engine.GetContractAddress(mCard), engine.GetTokenDecimals(mCard)); + reqETH.setID(67); + reqETH.setBlockchain(mCard.getBlockchain()); + + Infura_Request reqBalance = Infura_Request.GetBalance(mCard.getWallet()); + reqBalance.setID(67); + reqBalance.setBlockchain(mCard.getBlockchain()); + + Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(mCard.getWallet()); + reqNonce.setID(67); + reqNonce.setBlockchain(mCard.getBlockchain()); + updateETH.execute(reqETH, reqNonce, reqBalance); + + + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(mCard.getWallet(), "basic-attention-token", "ethereum"); + taskRate.execute(rate); + } + + if (needResendTX) { + SendTransaction(LastSignStorage.getTxForSend(mCard.getWallet())); + } + } + + void doShareWallet(boolean useURI) { + if (useURI) { + String txtShare = CoinEngineFactory.Create(mCard.getBlockchain()).getShareWalletURI(mCard).toString(); + //String txtShare = Blockchain.getShareWalletURI(mCard).toString(); + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_SUBJECT, "Wallet address"); + intent.putExtra(Intent.EXTRA_TEXT, txtShare); + + PackageManager packageManager = getActivity().getPackageManager(); + List activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL); + boolean isIntentSafe = activities.size() > 0; + + if (isIntentSafe) { + String title = "Share wallet address with:"; + // Create intent to show chooser + Intent chooser = Intent.createChooser(intent, title); + // Verify the intent will resolve to at least one activity + if (intent.resolveActivity(getActivity().getPackageManager()) != null) { + startActivity(chooser); + } + } else { + ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE); + clipboard.setPrimaryClip(ClipData.newPlainText(txtShare, txtShare)); + Toast.makeText(getContext(), "Copied to clipboard", Toast.LENGTH_LONG).show(); + } + } else { + String txtShare = mCard.getWallet(); + ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE); + clipboard.setPrimaryClip(ClipData.newPlainText(txtShare, txtShare)); + Toast.makeText(getContext(), "Copied to clipboard", Toast.LENGTH_LONG).show(); + } + + + } + + @Override + public View onCreateView(final LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_loaded_wallet, container, false); + + mNfcManager = new NfcManager(this.getActivity(), this); + + + // SwipeRefreshLayout + mSwipeRefreshLayout = v.findViewById(R.id.swipe_container); + mSwipeRefreshLayout.setOnRefreshListener(this); + + mCard = new Tangem_Card(getActivity().getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(getActivity().getIntent().getExtras().getBundle("Card")); + tvCardID = v.findViewById(R.id.tvCardID); + + tvBalance = v.findViewById(R.id.tvBalance); + tvOffline = v.findViewById(R.id.tvOffline); + + if (mCard.getBlockchain() == Blockchain.Token) { + //tvBalance.setLines(2); + tvBalance.setSingleLine(false); + } + + tvBalanceEquivalent = v.findViewById(R.id.tvBalanceEquivalent); + + tvWallet = v.findViewById(R.id.tvWallet); + tvWallet.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + doShareWallet(false); + } + }); + + tvInputs = v.findViewById(R.id.tvInputs); + lbInputs = v.findViewById(R.id.lbInputs); + + tvLastOutput = v.findViewById(R.id.tvLastOutput); + lbLastOutput = v.findViewById(R.id.lbLastOutput); + + final CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + + boolean visibleFlag = engine != null ? engine.InOutPutVisible() : true; + int visibleIOPuts = visibleFlag ? View.VISIBLE : View.GONE; + if (tvInputs != null) { + tvInputs.setVisibility(visibleIOPuts); + } + + if (lbInputs != null) { + lbInputs.setVisibility(visibleIOPuts); + } + + if (tvLastOutput != null) { + tvLastOutput.setVisibility(visibleIOPuts); + } + + if (lbLastOutput != null) { + lbLastOutput.setVisibility(visibleIOPuts); + } + + tvValidationNode = v.findViewById(R.id.tvValidationNode); + + progressBar = v.findViewById(R.id.progressBar); + + tvBlockchain = v.findViewById(R.id.tvBlockchain); + ivBlockchain = v.findViewById(R.id.imgBlockchain); + ivPIN = v.findViewById(R.id.imgPIN); + ivPIN2orSecurityDelay = v.findViewById(R.id.imgPIN2orSecurityDelay); + ivDeveloperVersion = v.findViewById(R.id.imgDeveloperVersion); + + ivQR = v.findViewById(R.id.qrWallet); + + + try { + ivQR.setImageBitmap(generateQrCode(engine.getShareWalletURI(mCard).toString())); + } catch (WriterException e) { + e.printStackTrace(); + } + ivQR.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + doShareWallet(true); + } + }); + + tvError = v.findViewById(R.id.tvError); + tvMessage = v.findViewById(R.id.tvMessage); + + tvIssuer = v.findViewById(R.id.tvIssuer); + tvIssuerData = v.findViewById(R.id.tvIssuerData); + + tvHeader = v.findViewById(R.id.tvHeader); + tvCaution = v.findViewById(R.id.tvCaution); + + tvSend = v.findViewById(R.id.tvSend); + + imgLookup = v.findViewById(R.id.imgLookup); + + if (imgLookup != null) { + + imgLookup.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!mCard.hasBalanceInfo()) { + return; + } + CoinEngine engineClick = CoinEngineFactory.Create(mCard.getBlockchain()); + + Intent browserIntent = new Intent(Intent.ACTION_VIEW, engineClick.getShareWalletURIExplorer(mCard)); + startActivity(browserIntent); + } + }); + + } + + tvLookup = v.findViewById(R.id.tvLookup); + + if (tvLookup != null) { + + tvLookup.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!mCard.hasBalanceInfo()) { + return; + } + CoinEngine engineClick = CoinEngineFactory.Create(mCard.getBlockchain()); + + Intent browserIntent = new Intent(Intent.ACTION_VIEW, engineClick.getShareWalletURIExplorer(mCard)); + startActivity(browserIntent); + } + }); + } + + if (tvSend != null) { + tvSend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!mCard.hasBalanceInfo()) { + return; + } else if (!engine.IsBalanceNotZero(mCard)) { + Toast.makeText(getContext(), "The wallet is empty", Toast.LENGTH_LONG).show(); + return; + } else if (!engine.IsBalanceAlterNotZero(mCard)) { + Toast.makeText(getContext(), "Not enough funds for transaction fee (gas)!", Toast.LENGTH_LONG).show(); + return; + } else if (engine.AwaitingConfirmation(mCard)) { + Toast.makeText(getContext(), "Please wait while previous transaction is confirmed in blockchain", Toast.LENGTH_LONG).show(); + return; + } else if (!engine.CheckUnspentTransaction(mCard)) { + Toast.makeText(getContext(), "Please wait for confirmation of incoming transaction!", Toast.LENGTH_LONG).show(); + return; + } else if (mCard.getRemainingSignatures() == 0) { + Toast.makeText(getContext(), "Card hasn't remaining signature!", Toast.LENGTH_LONG).show(); + return; + } + + Intent intent = new Intent(getContext(), PreparePaymentActivity.class); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_SEND_PAYMENT); + } + }); + } + + tvPurge = v.findViewById(R.id.tvPurge); + if (tvPurge != null) { + tvPurge.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showMenu(tvPurge); + } + } + ); + } + UpdateViews(); + + if (!mCard.hasBalanceInfo()) { + mSwipeRefreshLayout.setRefreshing(true); + mSwipeRefreshLayout.postDelayed(new Runnable() { + @Override + public void run() { + onRefresh(); + } + }, 1000); + } + return v; + } + + public void showMenu(View v) { + final PopupMenu popup = new PopupMenu(getActivity(), v); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.menu_loaded_wallet, popup.getMenu()); + + popup.getMenu().findItem(R.id.action_set_PIN1).setVisible(mCard.allowSwapPIN()); + popup.getMenu().findItem(R.id.action_reset_PIN1).setVisible(mCard.allowSwapPIN() && !mCard.useDefaultPIN1()); + popup.getMenu().findItem(R.id.action_set_PIN2).setVisible(mCard.allowSwapPIN2()); + popup.getMenu().findItem(R.id.action_reset_PIN2).setVisible(mCard.allowSwapPIN2() && !mCard.useDefaultPIN2()); + popup.getMenu().findItem(R.id.action_reset_PINs).setVisible(mCard.allowSwapPIN() && mCard.allowSwapPIN2() && !mCard.useDefaultPIN1() && !mCard.useDefaultPIN2()); + + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + + int id = item.getItemId(); + switch (id) { + case R.id.action_set_PIN1: + doSetPin(); + return true; + case R.id.action_reset_PIN1: + doResetPin(); + return true; + case R.id.action_set_PIN2: + doSetPin2(); + return true; + case R.id.action_reset_PIN2: + doResetPin2(); + return true; + case R.id.action_reset_PINs: + doResetPins(); + return true; + case R.id.action_purge: + doPurge(); + return true; + default: + return false; + } + } + }); + popup.show(); + } + + String newPIN = "", newPIN2 = ""; + + void doSetPin() { + requestPIN2Count = 0; + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestNewPIN.toString()); + newPIN = ""; + newPIN2 = ""; + startActivityForResult(intent, REQUEST_CODE_ENTER_NEW_PIN); + } + + void doResetPin() { + requestPIN2Count = 0; + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + newPIN = PINStorage.getDefaultPIN(); + newPIN2 = ""; + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); + } + + void doResetPin2() { + requestPIN2Count = 0; + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + newPIN = ""; + newPIN2 = PINStorage.getDefaultPIN2(); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); + } + + void doResetPins() { + requestPIN2Count = 0; + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + newPIN = PINStorage.getDefaultPIN(); + newPIN2 = PINStorage.getDefaultPIN2(); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); + } + + + void doSetPin2() { + requestPIN2Count = 0; + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestNewPIN2.toString()); + newPIN = ""; + newPIN2 = ""; + startActivityForResult(intent, REQUEST_CODE_ENTER_NEW_PIN2); + } + + void doPurge() { + requestPIN2Count = 0; + final CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + if (!mCard.hasBalanceInfo()) { + return; + } else if (engine.IsBalanceNotZero(mCard)) { + Toast.makeText(getContext(), "Cannot erase wallet with non-zero balance", Toast.LENGTH_LONG).show(); + return; + } + + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_PURGE); + } + + void UpdateViews() { + try { + if (timerHideErrorAndMessage != null) { + timerHideErrorAndMessage.cancel(); + timerHideErrorAndMessage = null; + } + tvCardID.setText(mCard.getCIDDescription()); + + if ((mCard.getError() == null || mCard.getError().isEmpty())) { + tvError.setVisibility(View.GONE); + tvError.setText(""); + } else { + tvError.setVisibility(View.VISIBLE); + tvError.setText(mCard.getError()); + } + + boolean needResendTX = LastSignStorage.getNeedTxSend(mCard.getWallet()); + + if ((mCard.getMessage() == null || mCard.getMessage().isEmpty()) && !needResendTX) { + tvMessage.setText(""); + tvMessage.setVisibility(View.GONE); + } else { + if (needResendTX) { + tvMessage.setText("Sending cached transaction..."); + } else { + tvMessage.setText(mCard.getMessage()); + } + tvMessage.setVisibility(View.VISIBLE); + + } + + + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + + if (engine.HasBalanceInfo(mCard) || mCard.getOfflineBalance() == null) { + if (mCard.getBlockchain() == Blockchain.Token) { + Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard)); + tvBalance.setText(html); + } else { + tvBalance.setText(engine.GetBalanceWithAlter(mCard)); + } + + tvBalanceEquivalent.setText(engine.GetBalanceEquivalent(mCard)); + tvOffline.setVisibility(View.INVISIBLE); + } else { + String offlineAmount = engine.ConvertByteArrayToAmount(mCard, mCard.getOfflineBalance()); + if (mCard.getBlockchain() == Blockchain.Token) { + tvBalance.setText("NOT IMPLEMENTED"); + } else { + tvBalance.setText(engine.GetAmountDescription(mCard, offlineAmount)); + } + + tvBalanceEquivalent.setText(engine.GetAmountEqualentDescriptor(mCard, offlineAmount)); + tvOffline.setVisibility(View.VISIBLE); + } + + if (!mCard.getAmountEquivalentDescriptionAvailable()) { + //tvBalanceEquivalent.setError("Service unavailable"); + } else { + tvBalanceEquivalent.setError(null); + } + + tvWallet.setText(mCard.getWallet()); + + tvInputs.setText(mCard.getInputsDescription()); + if (mCard.getLastInputDescription().contains("awaiting")) { + tvInputs.setTextColor(getContext().getResources().getColor(R.color.not_confirmed, getContext().getTheme())); + } else if (mCard.getLastInputDescription().contains("None")) { + tvInputs.setTextColor(getContext().getResources().getColor(R.color.primary_dark, getContext().getTheme())); + } else { + tvInputs.setTextColor(getContext().getResources().getColor(R.color.confirmed, getContext().getTheme())); + } + + if (tvOutputs != null) { + tvOutputs.setText(mCard.getOutputsDescription()); + } + + if (tvLastInput != null) { + tvLastInput.setText(mCard.getLastInputDescription()); + } + if (tvLastOutput != null) { + tvLastOutput.setText(mCard.getLastOutputDescription()); + } + + tvBlockchain.setText(mCard.getBlockchainName()); + ivBlockchain.setImageResource(mCard.getBlockchain().getImageResource(this.getContext(), mCard.getTokenSymbol())); + + if (tvValidationNode != null) { + tvValidationNode.setText(mCard.getValidationNodeDescription()); + } + + if (mCard.useDefaultPIN1()) { + ivPIN.setImageResource(R.drawable.unlock_pin1); + ivPIN.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "This banknote is protected by default PIN1 code", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivPIN.setImageResource(R.drawable.lock_pin1); + ivPIN.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "This banknote is protected by user's PIN1 code", Toast.LENGTH_LONG).show(); + } + }); + } + + if (mCard.getPauseBeforePIN2() > 0 && (mCard.useDefaultPIN2() || !mCard.useSmartSecurityDelay())) { + ivPIN2orSecurityDelay.setImageResource(R.drawable.timer); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), String.format("This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code", mCard.getPauseBeforePIN2() / 1000.0), Toast.LENGTH_LONG).show(); + } + }); + + } else if (mCard.useDefaultPIN2()) { + ivPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "This banknote is protected by default PIN2 code", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "This banknote is protected by user's PIN2 code", Toast.LENGTH_LONG).show(); + } + }); + } + + + if (mCard.useDevelopersFirmware()) { + ivDeveloperVersion.setImageResource(R.drawable.ic_developer_version); + ivDeveloperVersion.setVisibility(View.VISIBLE); + ivDeveloperVersion.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "Unlocked banknote, only for development use", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivDeveloperVersion.setVisibility(View.INVISIBLE); + } + + if (tvSend != null) { + if (mCard.hasBalanceInfo()) { + tvSend.setEnabled(true); + } else { + tvSend.setEnabled(false); + } + } + + if (tvPurge != null) { + if (mCard.hasBalanceInfo()) { + tvPurge.setEnabled(true); + } else { + tvPurge.setEnabled(false); + } + } + + tvIssuer.setText(mCard.getIssuerDescription()); + + timerHideErrorAndMessage = new Timer(); + + + timerHideErrorAndMessage.schedule(new TimerTask() { + @Override + public void run() { + tvError.post(new Runnable() { + @Override + public void run() { + tvMessage.setVisibility(View.GONE); + tvError.setVisibility(View.GONE); + mCard.setError(null); + mCard.setMessage(null); + } + }); + } + }, 5000); + + if (mCard.isReusable()) { + tvHeader.setText("REUSABLE WALLET"); + tvCaution.setVisibility(View.GONE); + } else { + if (mCard.getMaxSignatures() == mCard.getRemainingSignatures()) { + tvHeader.setText("BANKNOTE"); + tvCaution.setVisibility(View.GONE); + } else { + tvHeader.setText("NON-TRANSFERABLE BANKNOTE"); + tvCaution.setVisibility(View.VISIBLE); + } + } + + if (mCard.useDevelopersFirmware()) { + tvHeader.setText("DEVELOPER KIT"); + tvCaution.setVisibility(View.VISIBLE); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + Timer timerHideErrorAndMessage = null; + + public Intent prepareResultIntent() { + Intent data = new Intent(); + data.putExtra("UID", mCard.getUID()); + data.putExtra("Card", mCard.getAsBundle()); + return data; + } + + private void SendTransaction(String tx) { + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet || mCard.getBlockchain() == Blockchain.Token) { + ETHRequestTask task = new ETHRequestTask(mCard.getBlockchain()); + Infura_Request req = Infura_Request.SendTransaction(mCard.getWallet(), tx); + req.setID(67); + req.setBlockchain(mCard.getBlockchain()); + task.execute(req); + } else if (mCard.getBlockchain() == Blockchain.Bitcoin ||mCard.getBlockchain() == Blockchain.BitcoinTestNet) { + String nodeAddress = engine.GetNode(mCard); + int nodePort = engine.GetNodePort(mCard); + + UpdateWalletInfoTask connectTask = new UpdateWalletInfoTask(nodeAddress, nodePort); + connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx)); + } + else if (mCard.getBlockchain() == Blockchain.BitcoinCash ||mCard.getBlockchain() == Blockchain.BitcoinCashTestNet) { + String nodeAddress = engine.GetNode(mCard); + int nodePort = engine.GetNodePort(mCard); + + UpdateWalletInfoTask connectTask = new UpdateWalletInfoTask(nodeAddress, nodePort); + connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx)); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQUEST_CODE_ENTER_NEW_PIN: + if (resultCode == Activity.RESULT_OK) { + if (data != null) { + if (data.getExtras().containsKey("confirmPIN")) { + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + newPIN = data.getStringExtra("newPIN"); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); + } else { + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("newPIN", data.getStringExtra("newPIN")); + intent.putExtra("mode", RequestPINActivity.Mode.ConfirmNewPIN.toString()); + startActivityForResult(intent, REQUEST_CODE_ENTER_NEW_PIN); + } + } + } + break; + case REQUEST_CODE_ENTER_NEW_PIN2: + if (resultCode == Activity.RESULT_OK) { + if (data != null) { + if (data.getExtras().containsKey("confirmPIN2")) { + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + newPIN2 = data.getStringExtra("newPIN2"); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); + } else { + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("newPIN2", data.getStringExtra("newPIN2")); + intent.putExtra("mode", RequestPINActivity.Mode.ConfirmNewPIN2.toString()); + startActivityForResult(intent, REQUEST_CODE_ENTER_NEW_PIN2); + } + } + } + break; + case REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN: + if (resultCode == Activity.RESULT_OK) { + if (newPIN.equals("")) { + newPIN = mCard.getPIN(); + } + if (newPIN2.equals("")) { + newPIN2 = PINStorage.getPIN2(); + } + + PINSwapWarningDialog dialog = (new PINSwapWarningDialog()); + dialog.activityFragment = this; + if (!PINStorage.isDefaultPIN(newPIN) || !PINStorage.isDefaultPIN2(newPIN2)) { + dialog.message = "If you forget your new PIN you will lose your money forever!"; + } else { + dialog.message = "If you use default PIN someone can steal your money!"; + } + dialog.show(getActivity().getFragmentManager(), "PINSwapWarningDialog"); + } + break; + + case REQUEST_CODE_SWAP_PIN: + if (resultCode == Activity.RESULT_OK) { + if (data == null) { + data = new Intent(); + + data.putExtra("UID", mCard.getUID()); + data.putExtra("Card", mCard.getAsBundle()); + data.putExtra("modification", "delete"); + } else { + data.putExtra("modification", "update"); + } + getActivity().setResult(Activity.RESULT_OK, data); + getActivity().finish(); + } else { + if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { + Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID")); + updatedCard.LoadFromBundle(data.getBundleExtra("Card")); + mCard = updatedCard; + } + if (resultCode == CreateNewWalletActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) { + requestPIN2Count++; + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN); + return; + } else { + if (data != null && data.getExtras().containsKey("message")) { + mCard.setError(data.getStringExtra("message")); + } + } + } + break; + case REQUEST_CODE_REQUEST_PIN2_FOR_PURGE: + if (resultCode == Activity.RESULT_OK) { + Intent intent = new Intent(getContext(), PurgeActivity.class); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_PURGE); + } + break; + case REQUEST_CODE_PURGE: + if (resultCode == Activity.RESULT_OK) { + if (data == null) { + data = new Intent(); + + data.putExtra("UID", mCard.getUID()); + data.putExtra("Card", mCard.getAsBundle()); + data.putExtra("modification", "delete"); + } else { + data.putExtra("modification", "update"); + } + getActivity().setResult(Activity.RESULT_OK, data); + getActivity().finish(); + } else { + if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { + Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID")); + updatedCard.LoadFromBundle(data.getBundleExtra("Card")); + mCard = updatedCard; + } + if (resultCode == CreateNewWalletActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) { + requestPIN2Count++; + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString()); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2_FOR_PURGE); + return; + } else { + if (data != null && data.getExtras().containsKey("message")) { + mCard.setError(data.getStringExtra("message")); + } + } + UpdateViews(); + } + break; + case REQUEST_CODE_SEND_PAYMENT: + if (resultCode == Activity.RESULT_OK) { + mSwipeRefreshLayout.postDelayed(new Runnable() { + @Override + public void run() { + onRefresh(); + } + }, 10000); + mSwipeRefreshLayout.setRefreshing(true); + mCard.clearInfo(); + UpdateViews(); + } + + if (data != null) { + if (data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) { + Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID")); + updatedCard.LoadFromBundle(data.getBundleExtra("Card")); + mCard = updatedCard; + } + if (data.getExtras().containsKey("message")) { + if (resultCode == Activity.RESULT_OK) { + mCard.setMessage(data.getStringExtra("message")); + } else { + mCard.setError(data.getStringExtra("message")); + } + } + UpdateViews(); + } + + break; + } + + } + + private void startSwapPINActivity() { + Intent intent = new Intent(getContext(), SwapPINActivity.class); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + intent.putExtra("newPIN", newPIN); + intent.putExtra("newPIN2", newPIN2); + startActivityForResult(intent, REQUEST_CODE_SWAP_PIN); + } + + public static Bitmap generateQrCode(String myCodeText) throws WriterException { + Hashtable hintMap = new Hashtable(); + hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // H = 30% damage + + QRCodeWriter qrCodeWriter = new QRCodeWriter(); + + int size = 256; + + BitMatrix bitMatrix = qrCodeWriter.encode(myCodeText, BarcodeFormat.QR_CODE, size, size, hintMap); + int width = bitMatrix.getWidth(); + Bitmap bmp = Bitmap.createBitmap(width, width, Bitmap.Config.RGB_565); + for (int x = 0; x < width; x++) { + for (int y = 0; y < width; y++) { + bmp.setPixel(y, x, bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE); + } + } + return bmp; + } + + + @Override + public void onResume() { + super.onResume(); + mNfcManager.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mNfcManager.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + for (UpdateWalletInfoTask ut : updateTasks) { + ut.cancel(true); + } + mNfcManager.onStop(); + } + + @Override + public void onTagDiscovered(Tag tag) { + try { + final IsoDep isoDep = IsoDep.get(tag); + if (isoDep == null) { + throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); + } + byte UID[] = tag.getId(); + String sUID = Util.byteArrayToHexString(UID); + if (!mCard.getUID().equals(sUID)) { + Log.d(logTag, "Invalid UID: " + sUID); + mNfcManager.IgnoreTag(isoDep.getTag()); + return; + } else { + Log.v(logTag, "UID: " + sUID); + } + + if (lastReadSuccess) { + isoDep.setTimeout(1000); + } else { + isoDep.setTimeout(65000); + } + //lastTag = tag; + verifyCardTask = new VerifyCardTask(getContext(), mCard, mNfcManager, isoDep, this); + verifyCardTask.start(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void ErrorOnUpdate(String message) { + mCard.setError("Cannot obtain data from blockchain"); + UpdateViews(); + } + + private class ETHRequestTask extends Infura_Task { + ETHRequestTask(Blockchain blockchain) { + super(blockchain); + } + + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + for (Infura_Request request : requests) { + try { + if (request.error == null) { + + if (request.isMethod(Infura_Request.METHOD_ETH_GetBalance)) { + try { + String balanceCap = request.getResultString(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); + Long balance = d.longValue(); + + mCard.setBalanceConfirmed(balance); + mCard.setBalanceUnconfirmed(0L); + if (mCard.getBlockchain() != Blockchain.Token) + mCard.setDecimalBalance(l.toString(10)); + mCard.setDecimalBalanceAlter(l.toString(10)); + + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + } + } else if (request.isMethod(Infura_Request.METHOD_ETH_Call)) { + try { + String balanceCap = request.getResultString(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + Long balance = l.longValue(); + + if (l.compareTo(BigInteger.ZERO) == 0) { + mCard.setBlockchainID(Blockchain.Ethereum.getID()); + mCard.addTokenToBlockchainName(); + mSwipeRefreshLayout.setRefreshing(false); + onRefresh(); + return; + } + + mCard.setBalanceConfirmed(balance); + mCard.setBalanceUnconfirmed(0L); + mCard.setDecimalBalance(l.toString(10)); + + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + } + } else if (request.isMethod(Infura_Request.METHOD_ETH_GetOutTransactionCount)) { + try { + String nonce = request.getResultString(); + nonce = nonce.substring(2); + BigInteger count = new BigInteger(nonce, 16); + + mCard.SetConfirmTXCount(count); + } catch (JSONException e) { + e.printStackTrace(); + } + } else if (request.isMethod(Infura_Request.METHOD_ETH_SendRawTransaction)) { + try { + String hashTX = ""; + + try { + String tmp = request.getResultString(); + hashTX = tmp; + } catch (JSONException e) { + JSONObject msg = request.getAnswer(); + JSONObject err = msg.getJSONObject("error"); + hashTX = err.getString("message"); + LastSignStorage.setLastMessage(mCard.getWallet(), hashTX); + ErrorOnUpdate("Failed to send transaction. Try again"); + return; + } + + if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { + hashTX = hashTX.substring(2); + } + BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ + LastSignStorage.setTxWasSend(mCard.getWallet()); + LastSignStorage.setLastMessage(mCard.getWallet(), ""); + Log.e("TX_RESULT", hashTX); + + + BigInteger nonce = mCard.GetConfirmTXCount(); + nonce.add(BigInteger.valueOf(1)); + mCard.SetConfirmTXCount(nonce); + Log.e("TX_RESULT", hashTX); + + } catch (Exception e) { + e.printStackTrace(); + ErrorOnUpdate("Failed to send transaction. Try again"); + } + } + UpdateViews(); + } else { + ErrorOnUpdate(request.error); + } + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + } + } + + if (updateTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); + } + } + + public void OnReadStart(CardProtocol cardProtocol) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setVisibility(View.VISIBLE); + progressBar.setProgress(5); + } + }); + } + + public void OnReadFinish(final CardProtocol cardProtocol) { + + verifyCardTask = null; + + if (cardProtocol != null) { + if (cardProtocol.getError() == null) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); + Intent intent = new Intent(getContext(), VerifyCardActivity.class); + // TODO обновить карту mCard + intent.putExtra("UID", cardProtocol.getCard().getUID()); + intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); + startActivityForResult(intent, REQUEST_CODE_VERIFY_CARD); + //addCard(cardProtocol.getCard()); + } + }); + } else { + // remove last UIDs because of error and no card read + progressBar.post(new Runnable() { + @Override + public void run() { + lastReadSuccess = false; + if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { + if (!NoExtendedLengthSupportDialog.allreadyShowed) { + new NoExtendedLengthSupportDialog().show(getActivity().getFragmentManager(), "NoExtendedLengthSupportDialog"); + } + } else { + Toast.makeText(getContext(), "Try to scan again", Toast.LENGTH_LONG).show(); + } + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + } + }); + } + } + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + public void OnReadProgress(CardProtocol protocol, final int progress) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(progress); + } + }); + } + + public void OnReadCancel() { + + verifyCardTask = null; + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + public void OnReadWait(int msec) { + WaitSecurityDelayDialog.OnReadWait(getActivity(), msec); + } + + @Override + public void OnReadBeforeRequest(int timeout) { + WaitSecurityDelayDialog.onReadBeforeRequest(getActivity(), timeout); + } + + @Override + public void OnReadAfterRequest() { + WaitSecurityDelayDialog.onReadAfterRequest(getActivity()); + } + + + private class RateInfoTask extends ExchangeTask { + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + for (ExchangeRequest request : requests) { + if (request.error == null) { + try { + + JSONArray arr = request.getAnswerList(); + for (int i = 0; i < arr.length(); ++i) { + JSONObject obj = arr.getJSONObject(i); + String currency = obj.getString("id"); + + boolean stop = false; + boolean stopAlter = false; + if (currency.equals(request.currency)) { + String usd = obj.getString("price_usd"); + + Float rate = Float.valueOf(usd); + mCard.setRate(rate); + UpdateViews(); + stop = true; + } + + if (currency.equals(request.currencyAlter)) { + String usd = obj.getString("price_usd"); + + Float rate = Float.valueOf(usd); + mCard.setRateAlter(rate); + UpdateViews(); + stopAlter = true; + } + + if (stop && stopAlter) { + break; + } + + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + } + } + } + + private class UpdateWalletInfoTask extends Electrum_Task { + public UpdateWalletInfoTask(String host, int port) { + super(host, port); + } + + + public UpdateWalletInfoTask(String host, int port, SharedData sharedData) { + super(host, port, sharedData); + } + + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + } + + @Override + protected void onCancelled() { + super.onCancelled(); + updateTasks.remove(this); + if (updateTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); + } + + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + Log.i("RequestWalletInfoTask", "onPostExecute[" + String.valueOf(updateTasks.size()) + "]"); + updateTasks.remove(this); + + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + + for (Electrum_Request request : requests) { + try { + if (request.error == null) { + if (request.isMethod(Electrum_Request.METHOD_GetBalance)) { + try { + String mWalletAddress = request.getParams().getString(0); + Long confBalance = request.getResult().getLong("confirmed"); + Long unconf = request.getResult().getLong("unconfirmed"); + if (sharedCounter != null) { + int counter = sharedCounter.requestCounter.incrementAndGet(); + if (counter != 1) { + continue; + } + } + + mCard.setBalanceConfirmed(confBalance); + mCard.setBalanceUnconfirmed(unconf); + mCard.setDecimalBalance(String.valueOf(confBalance)); + mCard.setValidationNodeDescription(getValidationNodeDescription()); + } catch (JSONException e) { + if (sharedCounter != null) { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if (errCounter >= sharedCounter.allRequest) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + } + } else { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + } + } + } else if (request.isMethod(Electrum_Request.METHOD_SendTransaction)) { + try { + String hashTX = request.getResultString(); + + try { + LastSignStorage.setLastMessage(mCard.getWallet(), hashTX); + if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { + hashTX = hashTX.substring(2); + } + BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ + LastSignStorage.setTxWasSend(mCard.getWallet()); + LastSignStorage.setLastMessage(mCard.getWallet(), ""); + Log.e("TX_RESULT", hashTX); + + } catch (Exception e) { + engine.SwitchNode(mCard); + ErrorOnUpdate("Failed to send transaction. Try again."); + } + + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate("Failed to send transaction. Try again."); + engine.SwitchNode(mCard); + } + } else if (request.isMethod(Electrum_Request.METHOD_ListUnspent)) { + try { + String mWalletAddress = request.getParams().getString(0); + + JSONArray jsUnspentArray = request.getResultArray(); + try { + mCard.getUnspentTransactions().clear(); + for (int i = 0; i < jsUnspentArray.length(); i++) { + JSONObject jsUnspent = jsUnspentArray.getJSONObject(i); + Tangem_Card.UnspentTransaction trUnspent = new Tangem_Card.UnspentTransaction(); + trUnspent.txID = jsUnspent.getString("tx_hash"); + trUnspent.Amount = jsUnspent.getInt("value"); + trUnspent.Height = jsUnspent.getInt("height"); + mCard.getUnspentTransactions().add(trUnspent); + } + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + } + + for (int i = 0; i < jsUnspentArray.length(); i++) { + JSONObject jsUnspent = jsUnspentArray.getJSONObject(i); + Integer height = jsUnspent.getInt("height"); + String hash = jsUnspent.getString("tx_hash"); + if (height != -1) { + String nodeAddress = engine.GetNextNode(mCard); + int nodePort = engine.GetNextNodePort(mCard); + UpdateWalletInfoTask updateWalletInfoTask = new UpdateWalletInfoTask(nodeAddress, nodePort); + + updateTasks.add(updateWalletInfoTask); + + updateWalletInfoTask.execute(Electrum_Request.GetHeader(mWalletAddress, String.valueOf(height)), + Electrum_Request.GetTransaction(mWalletAddress, hash)); + } + } + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + } + } else if (request.isMethod(Electrum_Request.METHOD_GetHistory)) { + try { + String mWalletAddress = request.getParams().getString(0); + + JSONArray jsHistoryArray = request.getResultArray(); + try { + mCard.getHistoryTransactions().clear(); + for (int i = 0; i < jsHistoryArray.length(); i++) { + JSONObject jsUnspent = jsHistoryArray.getJSONObject(i); + Tangem_Card.HistoryTransaction trHistory = new Tangem_Card.HistoryTransaction(); + trHistory.txID = jsUnspent.getString("tx_hash"); + trHistory.Height = jsUnspent.getInt("height"); + mCard.getHistoryTransactions().add(trHistory); + } + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + } + + for (int i = 0; i < jsHistoryArray.length(); i++) { + JSONObject jsUnspent = jsHistoryArray.getJSONObject(i); + Integer height = jsUnspent.getInt("height"); + String hash = jsUnspent.getString("tx_hash"); + if (height != -1) { + + String nodeAddress = engine.GetNode(mCard); + int nodePort = engine.GetNodePort(mCard); + UpdateWalletInfoTask updateWalletInfoTask = new UpdateWalletInfoTask(nodeAddress, nodePort); + updateTasks.add(updateWalletInfoTask); + + updateWalletInfoTask.execute(Electrum_Request.GetHeader(mWalletAddress, String.valueOf(height)), + Electrum_Request.GetTransaction(mWalletAddress, hash)); + } + + } + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + } + } else if (request.isMethod(Electrum_Request.METHOD_GetHeader)) { + try { + JSONObject jsHeader = request.getResult(); + try { + mCard.getHaedersInfo(); + mCard.UpdateHeaderInfo(new Tangem_Card.HeaderInfo( + jsHeader.getInt("block_height"), + jsHeader.getInt("timestamp"))); + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + + } + + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + + } + } else if (request.isMethod(Electrum_Request.METHOD_GetTransaction)) { + try { + + String txHash = request.TxHash; + String raw = request.getResultString(); + + List listTx = mCard.getUnspentTransactions(); + for (Tangem_Card.UnspentTransaction tx : listTx) { + if (tx.txID.equals(txHash)) { + tx.Raw = raw; + } + } + + List listHTx = mCard.getHistoryTransactions(); + for (Tangem_Card.HistoryTransaction tx : listHTx) { + if (tx.txID.equals(txHash)) { + tx.Raw = raw; + try { + ArrayList prevHashes = BTCUtils.getPrevTX(raw); + + boolean isOur = false; + for (byte[] hash : prevHashes) { + String checkID = BTCUtils.toHex(hash); + for (Tangem_Card.HistoryTransaction txForCheck : listHTx) { + if (txForCheck.txID == checkID) { + isOur = true; + } + } + } + + tx.isInput = !isOur; + } catch (BitcoinException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + } + Log.e("TX", raw); + } + } + + } catch (JSONException e) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + engine.SwitchNode(mCard); + } + } + UpdateViews(); + } else { + if (sharedCounter != null) { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if (errCounter >= sharedCounter.allRequest) { + ErrorOnUpdate(request.error); + engine.SwitchNode(mCard); + + } + } else { + ErrorOnUpdate(request.error); + engine.SwitchNode(mCard); + + } + + } + } catch (JSONException e) { + if (sharedCounter != null) { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if (errCounter >= sharedCounter.allRequest) { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + } + } else { + e.printStackTrace(); + ErrorOnUpdate(e.toString()); + } + } + } + if (updateTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); + + } + } + + public static class PINSwapWarningDialog extends DialogFragment { + + LoadedWalletActivityFragment activityFragment = null; + String message; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + return new AlertDialog.Builder(getActivity()) + .setIcon(R.drawable.tangem_logo_small_new) + .setTitle("Your money is at risk!") + .setMessage(message) + .setCancelable(true) + .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + PINSwapWarningDialog.this.dismiss(); + } + }) + .setPositiveButton("Continue", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + if (activityFragment != null) + activityFragment.startSwapPINActivity(); + } + } + ) + .create(); + } + + @Override + public void onCancel(DialogInterface dialog) { + super.onCancel(dialog); + } + } + +} diff --git a/app/src/main/java/com/tangem/wallet/LogFileProvider.java b/app/src/main/java/com/tangem/wallet/LogFileProvider.java index a4f710d5fc..7c09580b29 100644 --- a/app/src/main/java/com/tangem/wallet/LogFileProvider.java +++ b/app/src/main/java/com/tangem/wallet/LogFileProvider.java @@ -1,108 +1,108 @@ -package com.tangem.wallet; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.UriMatcher; -import android.database.Cursor; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.util.Log; - -import java.io.File; -import java.io.FileNotFoundException; - -/** - * Created by dvol on 15.02.2018. - */ - -public class LogFileProvider extends ContentProvider { - - private static final String CLASS_NAME = "LogFileProvider"; - - // The authority is the symbolic name for the provider class - public static final String AUTHORITY = "com.tangem.wallet.LogFileProvider"; - - // UriMatcher used to match against incoming requests - private UriMatcher uriMatcher; - - @Override - public boolean onCreate() { - uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); - - // Add a URI to the matcher which will match against the form - // 'content://it.my.app.LogFileProvider/*' - // and return 1 in the case that the incoming Uri matches this pattern - uriMatcher.addURI(AUTHORITY, "*", 1); - - return true; - } - - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) - throws FileNotFoundException { - - String LOG_TAG = CLASS_NAME+"-oF"; - - Log.v(LOG_TAG, - "Called with uri: '" + uri + "'." + uri.getLastPathSegment()); - - // Check incoming Uri against the matcher - switch (uriMatcher.match(uri)) { - - // If it returns 1 - then it matches the Uri defined in onCreate - case 1: - - // The desired file name is specified by the last segment of the - // path - // E.g. - // 'content://it.my.app.LogFileProvider/Test.txt' - // Take this and build the path to the file - String fileLocation = getContext().getCacheDir() + File.separator - + uri.getLastPathSegment(); - - // Create & return a ParcelFileDescriptor pointing to the file - // Note: I don't care what mode they ask for - they're only getting - // read only - ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File( - fileLocation), ParcelFileDescriptor.MODE_READ_ONLY); - return pfd; - - // Otherwise unrecognised Uri - default: - Log.v(LOG_TAG, "Unsupported uri: '" + uri + "'."); - throw new FileNotFoundException("Unsupported uri: " - + uri.toString()); - } - } - - // ////////////////////////////////////////////////////////////// - // Not supported / used / required for this example - // ////////////////////////////////////////////////////////////// - - @Override - public int update(Uri uri, ContentValues contentvalues, String s, - String[] as) { - return 0; - } - - @Override - public int delete(Uri uri, String s, String[] as) { - return 0; - } - - @Override - public Uri insert(Uri uri, ContentValues contentvalues) { - return null; - } - - @Override - public String getType(Uri uri) { - return null; - } - - @Override - public Cursor query(Uri uri, String[] projection, String s, String[] as1, - String s1) { - return null; - } -} +package com.tangem.wallet; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; + +/** + * Created by dvol on 15.02.2018. + */ + +public class LogFileProvider extends ContentProvider { + + private static final String CLASS_NAME = "LogFileProvider"; + + // The authority is the symbolic name for the provider class + public static final String AUTHORITY = "com.tangem.wallet.LogFileProvider"; + + // UriMatcher used to match against incoming requests + private UriMatcher uriMatcher; + + @Override + public boolean onCreate() { + uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + + // Add a URI to the matcher which will match against the form + // 'content://it.my.app.LogFileProvider/*' + // and return 1 in the case that the incoming Uri matches this pattern + uriMatcher.addURI(AUTHORITY, "*", 1); + + return true; + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) + throws FileNotFoundException { + + String LOG_TAG = CLASS_NAME+"-oF"; + + Log.v(LOG_TAG, + "Called with uri: '" + uri + "'." + uri.getLastPathSegment()); + + // Check incoming Uri against the matcher + switch (uriMatcher.match(uri)) { + + // If it returns 1 - then it matches the Uri defined in onCreate + case 1: + + // The desired file name is specified by the last segment of the + // path + // E.g. + // 'content://it.my.app.LogFileProvider/Test.txt' + // Take this and build the path to the file + String fileLocation = getContext().getCacheDir() + File.separator + + uri.getLastPathSegment(); + + // Create & return a ParcelFileDescriptor pointing to the file + // Note: I don't care what mode they ask for - they're only getting + // read only + ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File( + fileLocation), ParcelFileDescriptor.MODE_READ_ONLY); + return pfd; + + // Otherwise unrecognised Uri + default: + Log.v(LOG_TAG, "Unsupported uri: '" + uri + "'."); + throw new FileNotFoundException("Unsupported uri: " + + uri.toString()); + } + } + + // ////////////////////////////////////////////////////////////// + // Not supported / used / required for this example + // ////////////////////////////////////////////////////////////// + + @Override + public int update(Uri uri, ContentValues contentvalues, String s, + String[] as) { + return 0; + } + + @Override + public int delete(Uri uri, String s, String[] as) { + return 0; + } + + @Override + public Uri insert(Uri uri, ContentValues contentvalues) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Cursor query(Uri uri, String[] projection, String s, String[] as1, + String s1) { + return null; + } +} diff --git a/app/src/main/java/com/tangem/wallet/Logger.java b/app/src/main/java/com/tangem/wallet/Logger.java index 7b2d67f28c..daf5c146a6 100644 --- a/app/src/main/java/com/tangem/wallet/Logger.java +++ b/app/src/main/java/com/tangem/wallet/Logger.java @@ -1,285 +1,285 @@ -package com.tangem.wallet; - -import android.content.Context; -import android.util.Log; - -import com.tangem.cardReader.Util; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Date; - -public class Logger { - - public static File collectLogs(Context context) { - File f = new File(context.getCacheDir().getAbsolutePath() + "/Wallet_" + Util.formatDateTimeToFileName(new Date()) + ".log"); - try { - if (f.createNewFile()) { - f.setReadable(true); - FileWriter fileWriter = new FileWriter(f, true); - Process process = Runtime.getRuntime().exec("logcat -d -b main -v time"); - try { - InputStream is = process.getInputStream(); - InputStreamReader isr = new InputStreamReader(is); - BufferedReader bufferedReader = new BufferedReader(isr); - BufferedWriter buf = new BufferedWriter(fileWriter); - buf.append("Tangem Wallet logs"); - buf.newLine(); - - int i = 0; - String line; - while ((line = bufferedReader.readLine()) != null) { - buf.append(line); - buf.newLine(); - i++; - } - Log.e("Logger", String.format("%d log lines collected", i)); - buf.newLine(); - buf.flush(); - buf.close(); - - } finally { - process.destroy(); - } - - return f; - } - } catch (Exception e) { - e.printStackTrace(); - } - - return null; - } - - -} - -//public class Logger { -// -// public static File[] getLastLogFiles() { -// File path = new File(Environment.getExternalStorageDirectory(), "Tangem/logs"); -// if (!path.exists()) { -// return null; -// } -// File[] files = path.listFiles(); -// Arrays.sort(files, new Comparator() { -// @Override -// public int compare(File o1, File o2) { -// if (o1.lastModified() < o2.lastModified()) { -// return -1; -// } else if (o1.lastModified() > o2.lastModified()) { -// return 1; -// } -// return 0; -// } -// }); -// if (files.length < 5) return files; -// return Arrays.copyOfRange(files, files.length - 5, files.length); -// } -// -// private static File logFile = null; -// -// private static void initLogFile(Context context) { -// try { -// File path = new File(Environment.getExternalStorageDirectory(), "Tangem/logs"); -// if (!path.exists()) { -// path.mkdirs(); -// MediaScannerConnection.scanFile(context, new String[]{path.getParentFile().toString()}, null, null); -// } -// logFile = new File(path, String.format("wallet_%s.log", Util.formatDateTimeToFileName(new Date()))); -// logFile.createNewFile(); -// logFile.setReadable(true); -// -// // initiate media scan and put the new things into the path array to -// // make the scanner aware of the location and the files you want to see -// MediaScannerConnection.scanFile(context, new String[]{logFile.getAbsolutePath()}, null, null); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// -// } -// -// public static boolean isCurrent(File f) { -// if (f == null || logFile == null) return false; -// return f.getAbsolutePath().equals(logFile.getAbsolutePath()); -// } -// -// -// private static class LogCatThread extends Thread { -// private boolean Terminated; -// -// private static final Object oSync = new Object(); -// -// public void Terminate() { -// Terminated = true; -// synchronized (oSync) { -// oSync.notifyAll(); -// } -// try { -// join(1000); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// interrupt(); -// } -// } -// -// public void collectLogs(Writer out) { -// try { -// Process process = Runtime.getRuntime().exec("logcat -d -b main -v time"); -// try { -// InputStream is = process.getInputStream(); -// InputStreamReader isr = new InputStreamReader(is); -// BufferedReader bufferedReader = new BufferedReader(isr); -// try { -// -// try { -// //BufferedWriter for performance, true to set append to file flag -// BufferedWriter buf = new BufferedWriter(out); -// -// while (!Terminated && isr.ready()) { -// String line = bufferedReader.readLine(); -// buf.append(line); -// buf.newLine(); -// } -// //Log.i("Logger",String.format("%d lines added",i)); -// buf.newLine(); -// buf.close(); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// -// -// } catch (Exception e) { -// e.printStackTrace(); -// } -// -// } finally { -// process.destroy(); -// } -// } catch (IOException e) { -// e.printStackTrace(); -// } -// -// } -// -// @Override -// public void run() { -// Process process = null; -// try { -// if (logFile == null) return; -// process = Runtime.getRuntime().exec("logcat -b main -v time"); -// try { -// InputStream is = process.getInputStream(); -// InputStreamReader isr = new InputStreamReader(is); -// BufferedReader bufferedReader = new BufferedReader(isr); -// while (!Terminated) { -// try { -// synchronized (oSync) { -// oSync.wait(1000); -// } -// if (!logFile.exists()) { -// try { -// logFile.createNewFile(); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } -// try { -// //BufferedWriter for performance, true to set append to file flag -// BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true)); -// -// while (isr.ready()) { -// String line = bufferedReader.readLine(); -// buf.append(line); -// buf.newLine(); -// } -// //Log.i("Logger",String.format("%d lines added",i)); -// buf.newLine(); -// buf.close(); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// -// -// } catch (Exception e) { -// e.printStackTrace(); -// } -// } -// } finally { -// process.destroy(); -// } -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } -// } -// -// static LogCatThread t = new LogCatThread(); -// -// public static void StartSaveToFile(Activity activity) { -// try { -// if (logFile != null) return; -// -// verifyStoragePermissions(activity); -// initLogFile(activity.getApplicationContext()); -// t.start(); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// } -// -// public static void StopSaveToFile(Context context) { -// final Object oSync = new Object(); -// if (t.isAlive()) { -// t.Terminate(); -// } -// if (logFile != null) { -// MediaScannerConnection.scanFile(context, new String[]{logFile.getAbsolutePath().toString()}, null, new MediaScannerConnection.OnScanCompletedListener() { -// @Override -// public void onScanCompleted(String path, Uri uri) { -//// synchronized (oSync) { -//// oSync.notifyAll(); -//// } -// } -// }); -//// try { -//// synchronized (oSync) { -//// oSync.wait(10000); -// logFile = null; -//// } -//// } catch (InterruptedException e) { -//// e.printStackTrace(); -//// } -// } -// -// } -// -// // Storage Permissions -// private static final int REQUEST_EXTERNAL_STORAGE = 1; -// private static String[] PERMISSIONS_STORAGE = { -// Manifest.permission.READ_EXTERNAL_STORAGE, -// Manifest.permission.WRITE_EXTERNAL_STORAGE -// }; -// -// //Checks if the app has permission to write to device storage -// //If the app does not has permission then the user will be prompted to grant permissions -// public static void verifyStoragePermissions(Activity activity) { -// // Check if we have write permission -// int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); -// -// if (permission != PackageManager.PERMISSION_GRANTED) { -// // We don't have permission so prompt the user -// ActivityCompat.requestPermissions( -// activity, -// PERMISSIONS_STORAGE, -// REQUEST_EXTERNAL_STORAGE -// ); -// } -// } -// -//} - +package com.tangem.wallet; + +import android.content.Context; +import android.util.Log; + +import com.tangem.cardReader.Util; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Date; + +public class Logger { + + public static File collectLogs(Context context) { + File f = new File(context.getCacheDir().getAbsolutePath() + "/Wallet_" + Util.formatDateTimeToFileName(new Date()) + ".log"); + try { + if (f.createNewFile()) { + f.setReadable(true); + FileWriter fileWriter = new FileWriter(f, true); + Process process = Runtime.getRuntime().exec("logcat -d -b main -v time"); + try { + InputStream is = process.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader bufferedReader = new BufferedReader(isr); + BufferedWriter buf = new BufferedWriter(fileWriter); + buf.append("Tangem Wallet logs"); + buf.newLine(); + + int i = 0; + String line; + while ((line = bufferedReader.readLine()) != null) { + buf.append(line); + buf.newLine(); + i++; + } + Log.e("Logger", String.format("%d log lines collected", i)); + buf.newLine(); + buf.flush(); + buf.close(); + + } finally { + process.destroy(); + } + + return f; + } + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + +} + +//public class Logger { +// +// public static File[] getLastLogFiles() { +// File path = new File(Environment.getExternalStorageDirectory(), "Tangem/logs"); +// if (!path.exists()) { +// return null; +// } +// File[] files = path.listFiles(); +// Arrays.sort(files, new Comparator() { +// @Override +// public int compare(File o1, File o2) { +// if (o1.lastModified() < o2.lastModified()) { +// return -1; +// } else if (o1.lastModified() > o2.lastModified()) { +// return 1; +// } +// return 0; +// } +// }); +// if (files.length < 5) return files; +// return Arrays.copyOfRange(files, files.length - 5, files.length); +// } +// +// private static File logFile = null; +// +// private static void initLogFile(Context context) { +// try { +// File path = new File(Environment.getExternalStorageDirectory(), "Tangem/logs"); +// if (!path.exists()) { +// path.mkdirs(); +// MediaScannerConnection.scanFile(context, new String[]{path.getParentFile().toString()}, null, null); +// } +// logFile = new File(path, String.format("wallet_%s.log", Util.formatDateTimeToFileName(new Date()))); +// logFile.createNewFile(); +// logFile.setReadable(true); +// +// // initiate media scan and put the new things into the path array to +// // make the scanner aware of the location and the files you want to see +// MediaScannerConnection.scanFile(context, new String[]{logFile.getAbsolutePath()}, null, null); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// +// } +// +// public static boolean isCurrent(File f) { +// if (f == null || logFile == null) return false; +// return f.getAbsolutePath().equals(logFile.getAbsolutePath()); +// } +// +// +// private static class LogCatThread extends Thread { +// private boolean Terminated; +// +// private static final Object oSync = new Object(); +// +// public void Terminate() { +// Terminated = true; +// synchronized (oSync) { +// oSync.notifyAll(); +// } +// try { +// join(1000); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// interrupt(); +// } +// } +// +// public void collectLogs(Writer out) { +// try { +// Process process = Runtime.getRuntime().exec("logcat -d -b main -v time"); +// try { +// InputStream is = process.getInputStream(); +// InputStreamReader isr = new InputStreamReader(is); +// BufferedReader bufferedReader = new BufferedReader(isr); +// try { +// +// try { +// //BufferedWriter for performance, true to set append to file flag +// BufferedWriter buf = new BufferedWriter(out); +// +// while (!Terminated && isr.ready()) { +// String line = bufferedReader.readLine(); +// buf.append(line); +// buf.newLine(); +// } +// //Log.i("Logger",String.format("%d lines added",i)); +// buf.newLine(); +// buf.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// +// +// } catch (Exception e) { +// e.printStackTrace(); +// } +// +// } finally { +// process.destroy(); +// } +// } catch (IOException e) { +// e.printStackTrace(); +// } +// +// } +// +// @Override +// public void run() { +// Process process = null; +// try { +// if (logFile == null) return; +// process = Runtime.getRuntime().exec("logcat -b main -v time"); +// try { +// InputStream is = process.getInputStream(); +// InputStreamReader isr = new InputStreamReader(is); +// BufferedReader bufferedReader = new BufferedReader(isr); +// while (!Terminated) { +// try { +// synchronized (oSync) { +// oSync.wait(1000); +// } +// if (!logFile.exists()) { +// try { +// logFile.createNewFile(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// try { +// //BufferedWriter for performance, true to set append to file flag +// BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true)); +// +// while (isr.ready()) { +// String line = bufferedReader.readLine(); +// buf.append(line); +// buf.newLine(); +// } +// //Log.i("Logger",String.format("%d lines added",i)); +// buf.newLine(); +// buf.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// +// +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// } finally { +// process.destroy(); +// } +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// } +// +// static LogCatThread t = new LogCatThread(); +// +// public static void StartSaveToFile(Activity activity) { +// try { +// if (logFile != null) return; +// +// verifyStoragePermissions(activity); +// initLogFile(activity.getApplicationContext()); +// t.start(); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// +// public static void StopSaveToFile(Context context) { +// final Object oSync = new Object(); +// if (t.isAlive()) { +// t.Terminate(); +// } +// if (logFile != null) { +// MediaScannerConnection.scanFile(context, new String[]{logFile.getAbsolutePath().toString()}, null, new MediaScannerConnection.OnScanCompletedListener() { +// @Override +// public void onScanCompleted(String path, Uri uri) { +//// synchronized (oSync) { +//// oSync.notifyAll(); +//// } +// } +// }); +//// try { +//// synchronized (oSync) { +//// oSync.wait(10000); +// logFile = null; +//// } +//// } catch (InterruptedException e) { +//// e.printStackTrace(); +//// } +// } +// +// } +// +// // Storage Permissions +// private static final int REQUEST_EXTERNAL_STORAGE = 1; +// private static String[] PERMISSIONS_STORAGE = { +// Manifest.permission.READ_EXTERNAL_STORAGE, +// Manifest.permission.WRITE_EXTERNAL_STORAGE +// }; +// +// //Checks if the app has permission to write to device storage +// //If the app does not has permission then the user will be prompted to grant permissions +// public static void verifyStoragePermissions(Activity activity) { +// // Check if we have write permission +// int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); +// +// if (permission != PackageManager.PERMISSION_GRANTED) { +// // We don't have permission so prompt the user +// ActivityCompat.requestPermissions( +// activity, +// PERMISSIONS_STORAGE, +// REQUEST_EXTERNAL_STORAGE +// ); +// } +// } +// +//} + diff --git a/app/src/main/java/com/tangem/wallet/LogoActivity.java b/app/src/main/java/com/tangem/wallet/LogoActivity.java index 913af1e0f0..7764ba0048 100644 --- a/app/src/main/java/com/tangem/wallet/LogoActivity.java +++ b/app/src/main/java/com/tangem/wallet/LogoActivity.java @@ -1,68 +1,68 @@ -package com.tangem.wallet; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -/** - * An example full-screen activity that shows and hides the system UI (i.e. - * status bar and navigation/system bar) with user interaction. - */ -public class LogoActivity extends AppCompatActivity { - - private final Runnable mHideRunnable = new Runnable() { - @Override - public void run() { - hide(); - } - }; - - ImageView imgLogo; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_logo); - - imgLogo= (ImageView) findViewById(R.id.imgLogo); - // Set up the user interaction to manually show or hide the system UI. - imgLogo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) {hide(); - } - }); - } - - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - - // Trigger the initial hide() shortly after the activity has been - // created, to briefly hint to the user that UI controls - // are available. - TextView AppVersion = (TextView) findViewById(R.id.AppVersion); - AppVersion.setText("BETA v." + BuildConfig.VERSION_NAME); - if( !getIntent().getBooleanExtra("skipAutoHide",false)) { - delayedHide(1000); - } - } - - private void hide() { - Intent intent=new Intent(getBaseContext(),MainActivity.class); - startActivity(intent); - finish(); - } - - /** - * Schedules a call to hide() in [delay] milliseconds, canceling any - * previously scheduled calls. - */ - private void delayedHide(int delayMillis) { - //imgLogo.removeCallbacks(mHideRunnable); - imgLogo.postDelayed(mHideRunnable, delayMillis); - } -} +package com.tangem.wallet; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +/** + * An example full-screen activity that shows and hides the system UI (i.e. + * status bar and navigation/system bar) with user interaction. + */ +public class LogoActivity extends AppCompatActivity { + + private final Runnable mHideRunnable = new Runnable() { + @Override + public void run() { + hide(); + } + }; + + ImageView imgLogo; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_logo); + + imgLogo= (ImageView) findViewById(R.id.imgLogo); + // Set up the user interaction to manually show or hide the system UI. + imgLogo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) {hide(); + } + }); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + + // Trigger the initial hide() shortly after the activity has been + // created, to briefly hint to the user that UI controls + // are available. + TextView AppVersion = (TextView) findViewById(R.id.AppVersion); + AppVersion.setText("BETA v." + BuildConfig.VERSION_NAME); + if( !getIntent().getBooleanExtra("skipAutoHide",false)) { + delayedHide(1000); + } + } + + private void hide() { + Intent intent=new Intent(getBaseContext(),MainActivity.class); + startActivity(intent); + finish(); + } + + /** + * Schedules a call to hide() in [delay] milliseconds, canceling any + * previously scheduled calls. + */ + private void delayedHide(int delayMillis) { + //imgLogo.removeCallbacks(mHideRunnable); + imgLogo.postDelayed(mHideRunnable, delayMillis); + } +} diff --git a/app/src/main/java/com/tangem/wallet/MainActivity.java b/app/src/main/java/com/tangem/wallet/MainActivity.java index c65f71fd97..7d7b2001ed 100644 --- a/app/src/main/java/com/tangem/wallet/MainActivity.java +++ b/app/src/main/java/com/tangem/wallet/MainActivity.java @@ -1,404 +1,404 @@ -package com.tangem.wallet; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.support.design.widget.FloatingActionButton; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.PopupMenu; -import android.util.Log; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.animation.Animation; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Transformation; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.scottyab.rootbeer.RootBeer; -import com.skyfishjy.library.RippleBackground; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - - -public class MainActivity extends AppCompatActivity implements PopupMenu.OnMenuItemClickListener { - - public static final int DIALOG_ENABLE_INTERNET = 1; - private static final int REQUEST_CODE_SEND_EMAIL = 2; - private String logTag = "MainActivity"; - - public interface OnCardsClean { - void doClean(); - } - -// public interface OnCreateNFCDialog { -// Dialog CreateNFCDialog(int id, AlertDialogWrapper.Builder builder, LayoutInflater li); -// } - - OnCardsClean onCardsClean; -// OnCreateNFCDialog onCreateNFCDialog; - NfcAdapter.ReaderCallback onNFCReaderCallback; - FloatingActionButton fab; - - - public void setOnCardsClean(OnCardsClean onCardsClean) { - this.onCardsClean = onCardsClean; - } - -// public void setOnCreateNFCDialog(OnCreateNFCDialog onCreateNFCDialog) { -// this.onCreateNFCDialog = onCreateNFCDialog; -// } - - public void setNfcAdapterReaderCallback(NfcAdapter.ReaderCallback callback) { - this.onNFCReaderCallback = callback; - } - - public void showCleanButton() { - - findViewById(R.id.tvTapPrompt).setVisibility(View.INVISIBLE); - } - - public void hideCleanButton() { - findViewById(R.id.tvTapPrompt).setVisibility(View.VISIBLE); - } - - public static class RootFoundDialog extends DialogFragment { - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - return new AlertDialog.Builder(getActivity()) - .setIcon(R.drawable.tangem_logo_small_new) - .setTitle("Your Android device is rooted. Security at risk!") - .setCancelable(false) - .setPositiveButton("Got it", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - } - } - ) - .create(); - } - - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - RootBeer rootBeer = new RootBeer(this); - if (rootBeer.isRootedWithoutBusyBoxCheck()) { - //we found indication of root - new RootFoundDialog().show(getFragmentManager(), "RootFoundDialog"); - } - - setContentView(R.layout.activity_main); - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); - - commonInit(getApplicationContext()); - - TextView tvNFCHint = findViewById(R.id.tvNFCHint); - if(tvNFCHint != null) - { -// tvNFCHint.setText("Scan a banknote with your\n" + PhoneUtility.GetPhoneName() + "\nas shown above"); - tvNFCHint.setText("Scan a banknote with your\n smartphone as shown above"); - } - - DeviceNFCAntennaLocation antenna = new DeviceNFCAntennaLocation(); - antenna.getAntennaLocation(); - final LinearLayout hand = findViewById(R.id.llHand); - final LinearLayout nfc = findViewById(R.id.llNFC); - final RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) hand.getLayoutParams(); - final RelativeLayout.LayoutParams lp2 = (RelativeLayout.LayoutParams) nfc.getLayoutParams(); - final float dp = getResources().getDisplayMetrics().density; - final float lm = dp*(69 + antenna.X * 75); - lp.topMargin = (int) (dp*(-100 + antenna.Y * 250)); - lp2.topMargin = (int) (dp*(-125 + antenna.Y * 250)); - nfc.setLayoutParams(lp2); - - Animation a = new Animation() { - - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - lp.leftMargin = (int)(lm * interpolatedTime); - hand.setLayoutParams(lp); - } - }; - a.setDuration(2000); // in ms - a.setInterpolator(new DecelerateInterpolator()); - hand.startAnimation(a); - - fab = findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - - showMenu(view); - } - }); - - MainActivityFragment mainActivityFragment=(MainActivityFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentMain); - if( mainActivityFragment.getCardListAdapter().getItemCount()>0 ) - { - showCleanButton(); - }else { - hideCleanButton(); - } - - final RippleBackground rippleBackground=(RippleBackground)findViewById(R.id.imNFC); - rippleBackground.startRippleAnimation(); - - - Intent intent = getIntent(); - if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()))) { - Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag != null && onNFCReaderCallback != null) { - onNFCReaderCallback.onTagDiscovered(tag); - } - } - } - - public static void commonInit(Context context) - { - if( PINStorage.needInit() ) { - PINStorage.Init(context); - } - if( LastSignStorage.needInit() ) { - LastSignStorage.Init(context); - } - } - - @Override - protected void onDestroy() { -// Logger.StopSaveToFile(getApplicationContext()); - super.onDestroy(); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()))) { - Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - if (tag != null && onNFCReaderCallback != null) { - onNFCReaderCallback.onTagDiscovered(tag); - } - } - - } - - @Override - public boolean onKeyDown(int keycode, KeyEvent e) { - switch (keycode) { - case KeyEvent.KEYCODE_MENU: - fab.requestFocus(); - showMenu(fab); - return true; - } - - return super.onKeyDown(keycode, e); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_main, menu); - if( BuildConfig.DEBUG ) { - for(int i=0; i 0) { - String[] fileNames = new String[filelocations.length]; - for (int i = 0; i < filelocations.length; i++) - fileNames[i] = filelocations[i].getAbsolutePath(); - zipFile = File.createTempFile("tangemLogs", ".zip", filelocations[0].getParentFile()); - Compress compress = new Compress(fileNames, zipFile.getAbsolutePath()); - compress.zip(); - Log.e(logTag, String.format("Send %d bytes zip with logs", zipFile.length())); - Uri attachment = Uri.parse("content://" + LogFileProvider.AUTHORITY + "/" - + zipFile.getName()); - - intent.putExtra(Intent.EXTRA_STREAM, attachment); - zipFile.deleteOnExit(); - } - - List activities = getPackageManager().queryIntentActivities(intent, 0); - boolean isIntentSafe = activities.size() > 0; - - if (isIntentSafe) { - startActivity(intent); - return; - } - } catch (Exception e) { - e.printStackTrace(); - } - - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == REQUEST_CODE_SEND_EMAIL) { - if (zipFile != null) { - zipFile.delete(); - zipFile = null; - } - } - super.onActivityResult(requestCode, resultCode, data); - } - - public void showMenu(View v) { - PopupMenu popup = new PopupMenu(this, v); - MenuInflater inflater = popup.getMenuInflater(); - inflater.inflate(R.menu.menu_main, popup.getMenu()); - if( BuildConfig.DEBUG ) { - for(int i=0; i0 ) + { + showCleanButton(); + }else { + hideCleanButton(); + } + + final RippleBackground rippleBackground=(RippleBackground)findViewById(R.id.imNFC); + rippleBackground.startRippleAnimation(); + + + Intent intent = getIntent(); + if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()))) { + Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag != null && onNFCReaderCallback != null) { + onNFCReaderCallback.onTagDiscovered(tag); + } + } + } + + public static void commonInit(Context context) + { + if( PINStorage.needInit() ) { + PINStorage.Init(context); + } + if( LastSignStorage.needInit() ) { + LastSignStorage.Init(context); + } + } + + @Override + protected void onDestroy() { +// Logger.StopSaveToFile(getApplicationContext()); + super.onDestroy(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()))) { + Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + if (tag != null && onNFCReaderCallback != null) { + onNFCReaderCallback.onTagDiscovered(tag); + } + } + + } + + @Override + public boolean onKeyDown(int keycode, KeyEvent e) { + switch (keycode) { + case KeyEvent.KEYCODE_MENU: + fab.requestFocus(); + showMenu(fab); + return true; + } + + return super.onKeyDown(keycode, e); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + if( BuildConfig.DEBUG ) { + for(int i=0; i 0) { + String[] fileNames = new String[filelocations.length]; + for (int i = 0; i < filelocations.length; i++) + fileNames[i] = filelocations[i].getAbsolutePath(); + zipFile = File.createTempFile("tangemLogs", ".zip", filelocations[0].getParentFile()); + Compress compress = new Compress(fileNames, zipFile.getAbsolutePath()); + compress.zip(); + Log.e(logTag, String.format("Send %d bytes zip with logs", zipFile.length())); + Uri attachment = Uri.parse("content://" + LogFileProvider.AUTHORITY + "/" + + zipFile.getName()); + + intent.putExtra(Intent.EXTRA_STREAM, attachment); + zipFile.deleteOnExit(); + } + + List activities = getPackageManager().queryIntentActivities(intent, 0); + boolean isIntentSafe = activities.size() > 0; + + if (isIntentSafe) { + startActivity(intent); + return; + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_CODE_SEND_EMAIL) { + if (zipFile != null) { + zipFile.delete(); + zipFile = null; + } + } + super.onActivityResult(requestCode, resultCode, data); + } + + public void showMenu(View v) { + PopupMenu popup = new PopupMenu(this, v); + MenuInflater inflater = popup.getMenuInflater(); + inflater.inflate(R.menu.menu_main, popup.getMenu()); + if( BuildConfig.DEBUG ) { + for(int i=0; i slCardUIDs = new ArrayList<>(); - private static final String logTag = "MainActivityFragment"; - private ProgressBar progressBar; - - private CardListAdapter mCardListAdapter; - - public CardListAdapter getCardListAdapter() { - return mCardListAdapter; - } - - private ReadCardInfoTask readCardInfoTask; - private SwipeRefreshLayout mSwipeRefreshLayout; //TODO: tmp - List requestTasks = new ArrayList<>(); - - public MainActivityFragment() { - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mCardListAdapter.onSaveInstanceState(outState); - outState.putStringArrayList("slCardUIDs", slCardUIDs); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View result = inflater.inflate(R.layout.fragment_main, container, false); - - mNfcManager = new NfcManager(this.getActivity(), this); - verifyPermissions(); - - progressBar = result.findViewById(R.id.progressBar); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - RecyclerView rvCards = result.findViewById(R.id.lvCards); - rvCards.setLayoutManager(new LinearLayoutManager(getContext())); - mCardListAdapter = new CardListAdapter(getActivity().getLayoutInflater(), savedInstanceState, this); - rvCards.setAdapter(mCardListAdapter); - if (savedInstanceState != null && savedInstanceState.containsKey("slCardUIDs")) { - slCardUIDs = savedInstanceState.getStringArrayList("slCardUIDs"); - } - - // SwipeRefreshLayout - mSwipeRefreshLayout = result.findViewById(R.id.swipe_container); //TODO: tmp - - if( mSwipeRefreshLayout!=null ) { - SwipeRefreshLayout.OnRefreshListener onRefreshListener = new SwipeRefreshLayout.OnRefreshListener() { - - @Override - public void onRefresh() { - //Update - // Showing refresh animation before making http call - - if (requestTasks.size() > 0) return; - - if (requestTasks.size() > 0) { //TODO: tmp - mSwipeRefreshLayout.setRefreshing(true); - } else { - mSwipeRefreshLayout.setRefreshing(false); - } - } - }; - mSwipeRefreshLayout.setOnRefreshListener(onRefreshListener); //TODO: tmp - } - - ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { - @Override - public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { - return false; - } - - @Override - public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { - //Remove swiped item from list and notify the RecyclerView - 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 = ""; - } - mCardListAdapter.removeCard(cardIndex); - if (mCardListAdapter.getItemCount() == 0 && getActivity().getClass() == MainActivity.class) { - ((MainActivity) getActivity()).hideCleanButton(); - } - } - }); - - itemTouchHelper.attachToRecyclerView(rvCards); - - if (getActivity() instanceof MainActivity) { - ((MainActivity) getActivity()).setOnCardsClean(this); -// ((MainActivity) getActivity()).setOnCreateNFCDialog(this); - ((MainActivity) getActivity()).setNfcAdapterReaderCallback(this); - } - - return result; - } - - private void verifyPermissions() { - NfcManager.verifyPermissions(getActivity()); - if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - Log.e("QRScanActivity", "User hasn't granted permission to use camera"); - ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS); - } - } - - int unsuccessReadCount = 0; - Tag lastTag = null; - - @Override - public void onTagDiscovered(Tag tag) { - try { - // get IsoDep handle and run cardReader thread - final IsoDep isoDep = IsoDep.get(tag); - if (isoDep == null) { - throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); - } - - byte UID[] = tag.getId(); - String sUID = Util.byteArrayToHexString(UID); - if (slCardUIDs.indexOf(sUID) != -1) { - Log.d(logTag, "Repeat UID: " + sUID); - mNfcManager.IgnoreTag(isoDep.getTag()); - return; - } else { - Log.v(logTag, "UID: " + sUID); - } - -// Log.e(logTag,"setTimeout("+String.valueOf(1000 + 3000 * unsuccessReadCount)+")"); - if (unsuccessReadCount < 2) { - isoDep.setTimeout(2000 + 5000 * unsuccessReadCount); - } else { - isoDep.setTimeout(90000); - } - lastTag = tag; - - readCardInfoTask = new ReadCardInfoTask(isoDep, this); - readCardInfoTask.start(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void onResume() { - super.onResume(); - - mNfcManager.onResume(); - } - - @Override - public void onPause() { - mNfcManager.onPause(); - if (readCardInfoTask != null) { - readCardInfoTask.cancel(true); - } - super.onPause(); - } - - @Override - public void onStop() { - // dismiss enable NFC dialog - mNfcManager.onStop(); - if (readCardInfoTask != null) { - readCardInfoTask.cancel(true); - } - for (RequestWalletInfoTask rt : requestTasks) { - rt.cancel(true); - } - super.onStop(); - } - - public void refreshCard(Tangem_Card card) { - CoinEngine engine = CoinEngineFactory.Create(card.getBlockchain()); - if (card.getBlockchain() == Blockchain.Ethereum || card.getBlockchain() == Blockchain.EthereumTestNet) { - ETHRequestTask task = new ETHRequestTask(card.getBlockchain()); - Infura_Request req = Infura_Request.GetBalance(card.getWallet()); - req.setID(67); - req.setBlockchain(card.getBlockchain()); - Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(card.getWallet()); - reqNonce.setID(67); - reqNonce.setBlockchain(card.getBlockchain()); - - task.execute(req, reqNonce); - - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "ethereum", "ethereum"); - taskRate.execute(rate); - - } else if (card.getBlockchain() == Blockchain.BitcoinTestNet || card.getBlockchain() == Blockchain.Bitcoin) { - SharedData data = new SharedData(SharedData.COUNT_REQUEST); - for (int i = 0; i < data.allRequest; ++i) { - - String nodeAddress = engine.GetNextNode(card); - int nodePort = engine.GetNextNodePort(card); - RequestWalletInfoTask connectTaskEx = new RequestWalletInfoTask(nodeAddress, nodePort, data); - connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(card.getWallet())); - } - - String nodeAddress = engine.GetNode(card); - int nodePort = engine.GetNodePort(card); - RequestWalletInfoTask task = new RequestWalletInfoTask(nodeAddress, nodePort); - if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(true); //TODO: tmp - requestTasks.add(task); - task.execute(Electrum_Request.ListUnspent(card.getWallet()), Electrum_Request.ListHistory(card.getWallet())); - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "bitcoin", "bitcoin"); - taskRate.execute(rate); - - } - else if (card.getBlockchain() == Blockchain.BitcoinCashTestNet || card.getBlockchain() == Blockchain.BitcoinCash) { - SharedData data = new SharedData(SharedData.COUNT_REQUEST); - for (int i = 0; i < data.allRequest; ++i) { - - String nodeAddress = engine.GetNextNode(card); - int nodePort = engine.GetNextNodePort(card); - RequestWalletInfoTask connectTaskEx = new RequestWalletInfoTask(nodeAddress, nodePort, data); - connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(card.getWallet())); - } - - String nodeAddress = engine.GetNode(card); - int nodePort = engine.GetNodePort(card); - RequestWalletInfoTask task = new RequestWalletInfoTask(nodeAddress, nodePort); - if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(true); //TODO: tmp - requestTasks.add(task); - task.execute(Electrum_Request.ListUnspent(card.getWallet()), Electrum_Request.ListHistory(card.getWallet())); - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "bitcoin-cash", "bitcoin-cash"); - taskRate.execute(rate); - - }else if (card.getBlockchain() == Blockchain.Token) { - ETHRequestTask updateETH = new ETHRequestTask(card.getBlockchain()); - Infura_Request reqETH = Infura_Request.GetTokenBalance(card.getWallet(), engine.GetContractAddress(card), engine.GetTokenDecimals(card)); - reqETH.setID(67); - reqETH.setBlockchain(card.getBlockchain()); - - - Infura_Request reqBalance = Infura_Request.GetBalance(card.getWallet()); - reqBalance.setID(67); - reqBalance.setBlockchain(card.getBlockchain()); - - - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "basic-attention-token", "ethereum"); - taskRate.execute(rate); - - Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(card.getWallet()); - reqNonce.setID(67); - reqNonce.setBlockchain(card.getBlockchain()); - - updateETH.execute(reqETH, reqNonce, reqBalance); - } - } - - public void addCard(final Tangem_Card card) { - if (mCardListAdapter != null) { - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - unsuccessReadCount = 0; - slCardUIDs.add(0, card.getUID()); - mCardListAdapter.addCard(card); - - CoinEngine engine = CoinEngineFactory.Create(card.getBlockchain()); - - if (card.getStatus() == Tangem_Card.Status.Loaded) { - - if (card.getBlockchain() == Blockchain.Ethereum || card.getBlockchain() == Blockchain.EthereumTestNet) { - ETHRequestTask task = new ETHRequestTask(card.getBlockchain()); - Infura_Request req = Infura_Request.GetBalance(card.getWallet()); - req.setID(67); - req.setBlockchain(card.getBlockchain()); - Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(card.getWallet()); - reqNonce.setID(67); - reqNonce.setBlockchain(card.getBlockchain()); - - task.execute(req, reqNonce); - - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "ethereum", "ethereum"); - taskRate.execute(rate); - - } else if (card.getBlockchain() == Blockchain.BitcoinTestNet || card.getBlockchain() == Blockchain.Bitcoin) { - SharedData data = new SharedData(SharedData.COUNT_REQUEST); - - for (int i = 0; i < data.allRequest; ++i) { - String nodeAddress = engine.GetNextNode(card); - int nodePort = engine.GetNextNodePort(card); - - RequestWalletInfoTask connectTaskEx = new RequestWalletInfoTask(nodeAddress, nodePort, data); - connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(card.getWallet())); - } - - String nodeAddress = engine.GetNode(card); - int nodePort = engine.GetNodePort(card); - - RequestWalletInfoTask task = new RequestWalletInfoTask(nodeAddress, nodePort); - if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(true); //TODO: tmp - - requestTasks.add(task); - task.execute(Electrum_Request.ListUnspent(card.getWallet()), Electrum_Request.ListHistory(card.getWallet())); - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "bitcoin", "bitcoin"); - taskRate.execute(rate); - - }else if (card.getBlockchain() == Blockchain.BitcoinCashTestNet || card.getBlockchain() == Blockchain.BitcoinCash) { - SharedData data = new SharedData(SharedData.COUNT_REQUEST); - - for (int i = 0; i < data.allRequest; ++i) { - String nodeAddress = engine.GetNextNode(card); - int nodePort = engine.GetNextNodePort(card); - - RequestWalletInfoTask connectTaskEx = new RequestWalletInfoTask(nodeAddress, nodePort, data); - connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(card.getWallet())); - } - - String nodeAddress = engine.GetNode(card); - int nodePort = engine.GetNodePort(card); - - RequestWalletInfoTask task = new RequestWalletInfoTask(nodeAddress, nodePort); - if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(true); //TODO: tmp - - requestTasks.add(task); - task.execute(Electrum_Request.ListUnspent(card.getWallet()), Electrum_Request.ListHistory(card.getWallet())); - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "bitcoin-cash", "bitcoin-cash"); - taskRate.execute(rate); - - } - else if (card.getBlockchain() == Blockchain.Token) { - ETHRequestTask updateETH = new ETHRequestTask(card.getBlockchain()); - Infura_Request reqETH = Infura_Request.GetTokenBalance(card.getWallet(), engine.GetContractAddress(card), engine.GetTokenDecimals(card)); - reqETH.setID(67); - reqETH.setBlockchain(card.getBlockchain()); - - - Infura_Request reqBalance = Infura_Request.GetBalance(card.getWallet()); - reqBalance.setID(67); - reqBalance.setBlockchain(card.getBlockchain()); - - - RateInfoTask taskRate = new RateInfoTask(); - ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "basic-attention-token", "ethereum"); - taskRate.execute(rate); - - Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(card.getWallet()); - reqNonce.setID(67); - reqNonce.setBlockchain(card.getBlockchain()); - - updateETH.execute(reqETH, reqNonce, reqBalance); - } - } - if (getActivity().getClass() == MainActivity.class) { - ((MainActivity) getActivity()).showCleanButton(); - } - } - }); - } - } - - @Override - public void onViewCard(Bundle cardInfo) { - - if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(false); //TODO: tmp - - String UID = cardInfo.getString("UID"); - Tangem_Card card = new Tangem_Card(UID); - card.LoadFromBundle(cardInfo.getBundle("Card")); - - - Intent intent; - - if (card.getStatus() == Tangem_Card.Status.Empty) { - intent = new Intent(getActivity(), EmptyWalletActivity.class); - } else if (card.getStatus() == Tangem_Card.Status.Loaded) { - intent = new Intent(getActivity(), LoadedWalletActivity.class); - } else if (card.getStatus() == Tangem_Card.Status.NotPersonalized || card.getStatus() == Tangem_Card.Status.Purged) { - return; - } else { - intent = new Intent(getActivity(), LoadedWalletActivity.class); - } - - intent.putExtras(cardInfo); - startActivityForResult(intent, REQUEST_CODE_SHOW_CARD_ACTIVITY); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - Log.d(logTag, "ActivityResult: requestCode = " + requestCode + ", resultCode = " + resultCode); - // если пришло ОК - if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_SHOW_CARD_ACTIVITY) { - if (data != null && data.getExtras().containsKey("UID")) { - final Tangem_Card card = new Tangem_Card(data.getStringExtra("UID")); - card.LoadFromBundle(data.getBundleExtra("Card")); - if (data.getStringExtra("modification").equals("delete")) { - mCardListAdapter.removeCard(card); - for (int i = 0; i < slCardUIDs.size(); i++) { - if (slCardUIDs.get(i).equals(card.getUID())) { - slCardUIDs.remove(i); - break; - } - } - if (mCardListAdapter.getItemCount() == 0 && getActivity().getClass() == MainActivity.class) { - ((MainActivity) getActivity()).hideCleanButton(); - } - } else if (data.getStringExtra("modification").equals("update")) { - mCardListAdapter.updateCard(card); - } else if (data.getStringExtra("modification").equals("updateAndViewCard")) { - mCardListAdapter.updateCard(card); - onViewCard(data.getExtras()); - } - - } - } else if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_ENTER_PIN_ACTIVITY) { - if (lastTag != null) onTagDiscovered(lastTag); - } - } - - @Override - public void doClean() { - mCardListAdapter.clearCards(); - slCardUIDs.clear(); - for (RequestWalletInfoTask rt : requestTasks) { - rt.cancel(true); - } - lastRead_UID = ""; - } - - public void doEnterPIN() { - Intent intent = new Intent(getContext(), RequestPINActivity.class); - intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN.toString()); - startActivityForResult(intent, REQUEST_CODE_ENTER_PIN_ACTIVITY); - } - - private static String lastRead_UID = ""; - private static ArrayList lastRead_UnsuccessfullPINs = new ArrayList<>(); - private static Tangem_Card.EncryptionMode lastRead_Encryption = null; - - private class ReadCardInfoTask extends Thread { - - - IsoDep mIsoDep; - CardProtocol.Notifications mNotifications; - private boolean isCancelled = false; - - ReadCardInfoTask(IsoDep isoDep, CardProtocol.Notifications notifications) { - mIsoDep = isoDep; - mNotifications = notifications; - } - - @Override - public void run() { - if (mIsoDep == null) { - return; - } - try { - // for Samsung's bugs - - // Workaround for the Samsung Galaxy S5 (since the - // first connection always hangs on transceive). - int timeout = mIsoDep.getTimeout(); - mIsoDep.connect(); - mIsoDep.close(); - mIsoDep.connect(); - mIsoDep.setTimeout(timeout); - try { - CardProtocol protocol = new CardProtocol(getContext(), mIsoDep, mNotifications); - mNotifications.OnReadStart(protocol); - try { - mNotifications.OnReadProgress(protocol, 5); - - byte[] UID = mIsoDep.getTag().getId(); - String sUID = Util.byteArrayToHexString(UID); - if (!lastRead_UID.equals(sUID)) { - lastRead_UID = sUID; - lastRead_UnsuccessfullPINs.clear(); - lastRead_Encryption = null; - } - - Log.i("ReadCardInfoTask", "[-- Start read card info --]"); - - if (isCancelled) return; - - protocol.setPIN(PINStorage.getDefaultPIN()); - protocol.clearReadResult(); - - if (lastRead_Encryption == null) { - Log.i("ReadCardInfoTask", "Try get supported encryption mode"); - protocol.run_GetSupportedEncryption(); - } else { - Log.i("ReadCardInfoTask", "Use already defined encryption mode: " + lastRead_Encryption.name()); - protocol.getCard().encryptionMode = lastRead_Encryption; - } - - if (protocol.haveReadResult()) { - //already have read result (obtained while get supported encryption), only read issuer data and define offline balance - protocol.parseReadResult(); - protocol.run_ReadOrWriteIssuerDataAndDefineOfflineBalance(); - mNotifications.OnReadProgress(protocol, 60); - PINStorage.setLastUsedPIN(protocol.getCard().getPIN()); - } else { - //don't have read result - may be don't get supported encryption on this try, need encryption or need another PIN - if (lastRead_Encryption == null) { - // we try get supported encryption on this time - lastRead_Encryption = protocol.getCard().encryptionMode; - if (protocol.getCard().encryptionMode == Tangem_Card.EncryptionMode.None) { - // default pin not accepted - lastRead_UnsuccessfullPINs.add(PINStorage.getDefaultPIN()); - } - } - - boolean pinFound = false; - for (String PIN : PINStorage.getPINs()) { - Log.e("ReadCardInfoTask", "PIN: " + PIN); - - boolean skipPin = false; - for (int i = 0; i < lastRead_UnsuccessfullPINs.size(); i++) { - if (lastRead_UnsuccessfullPINs.get(i).equals(PIN)) { - skipPin = true; - break; - } - } - - if (skipPin) { - Log.e("ReadCardInfoTask", "Skip PIN - already checked before"); - continue; - } - - try { - protocol.setPIN(PIN); - if (protocol.getCard().encryptionMode != Tangem_Card.EncryptionMode.None) { - protocol.CreateProtocolKey(); - } - protocol.run_Read(); - mNotifications.OnReadProgress(protocol, 60); - PINStorage.setLastUsedPIN(PIN); - pinFound = true; - protocol.getCard().setPIN(PIN); - break; - } catch (CardProtocol.TangemException_InvalidPIN e) { - Log.e(logTag, e.getMessage()); - lastRead_UnsuccessfullPINs.add(PIN); - } - } - if (!pinFound) { - throw new CardProtocol.TangemException_InvalidPIN("No valid PIN found!"); - } - } - - protocol.run_CheckPIN2isDefault(); - - } catch (Exception e) { - e.printStackTrace(); - protocol.setError(e); - - } finally { - Log.i("ReadCardInfoTask", "[-- Finish read card info --]"); - mNotifications.OnReadFinish(protocol); - } - } finally { - mNfcManager.IgnoreTag(mIsoDep.getTag()); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - void cancel(Boolean AllowInterrupt) { - try { - if (this.isAlive()) { - isCancelled = true; - join(500); - } - if (this.isAlive() && AllowInterrupt) { - interrupt(); - mNotifications.OnReadCancel(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - } - - public void OnReadStart(CardProtocol cardProtocol) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setVisibility(View.VISIBLE); - progressBar.setProgress(5); - } - }); - } - - public void OnReadFinish(final CardProtocol cardProtocol) { - - readCardInfoTask = null; - - if (cardProtocol != null) { - if (cardProtocol.getError() == null) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); - addCard(cardProtocol.getCard()); - } - }); - } else { - // remove last UIDs because of error and no card read - progressBar.post(new Runnable() { - @Override - public void run() { - Toast.makeText(getContext(), "Try to scan again", Toast.LENGTH_LONG).show(); - unsuccessReadCount++; - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - 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(getActivity().getFragmentManager(), "NoExtendedLengthSupportDialog"); - } - } else { - lastTag = null; - } - } - }); - } - } - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - public void OnReadProgress(CardProtocol protocol, final int progress) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(progress); - } - }); - } - - public void OnReadCancel() { - - readCardInfoTask = null; - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - - public void OnReadWait(final int msec) { - WaitSecurityDelayDialog.OnReadWait(getActivity(), msec); - } - - @Override - public void OnReadBeforeRequest(int timeout) { - WaitSecurityDelayDialog.onReadBeforeRequest(getActivity(), timeout); - } - - @Override - public void OnReadAfterRequest() { - WaitSecurityDelayDialog.onReadAfterRequest(getActivity()); - } - - private class RequestWalletInfoTask extends Electrum_Task { - public RequestWalletInfoTask(String host, int port) { - super(host, port); - } - - - public RequestWalletInfoTask(String host, int port, SharedData sharedData) { - super(host, port, sharedData); - } - - @Override - protected void onProgressUpdate(Integer... values) { - super.onProgressUpdate(values); - } - - @Override - protected void onCancelled() { - super.onCancelled(); - requestTasks.remove(this); - if ( mSwipeRefreshLayout!=null && requestTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); //TODO: tmp - - } - - void FinishWithError(String wallet, String message) { - mCardListAdapter.UpdateWalletError(wallet, "Cannot obtain data from blockchain"); - } - - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - requestTasks.remove(this); - Log.i("RequestWalletInfoTask", "onPostExecute[" + String.valueOf(requests.size()) + "]"); - - CoinEngine engine = CoinEngineFactory.Create(Blockchain.Bitcoin); - - for (Electrum_Request request : requests) { - try { - - if (request.error == null) { - if (request.isMethod(Electrum_Request.METHOD_GetBalance)) { - try { - Long conf = request.getResult().getLong("confirmed"); - Long unconf = request.getResult().getLong("unconfirmed"); - if (sharedCounter != null) { - int counter = sharedCounter.requestCounter.incrementAndGet(); - if (counter != 1) { - continue; - } - } - String mWalletAddress = request.getParams().getString(0); - mCardListAdapter.UpdateWalletBalance(mWalletAddress, conf, unconf, getValidationNodeDescription()); - } catch (JSONException e) { - if (sharedCounter != null) { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if (errCounter == sharedCounter.allRequest) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - engine.SwitchNode(null); - } - } else { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - engine.SwitchNode(null); - } - } - } else if (request.isMethod(Electrum_Request.METHOD_ListUnspent)) { - try { - String mWalletAddress = request.getParams().getString(0); - mCardListAdapter.UpdateWalletUnspent(mWalletAddress, request.getResultArray()); - - JSONArray unspentList = request.getResultArray(); - - for (int i = 0; i < unspentList.length(); i++) { - JSONObject jsUnspent = unspentList.getJSONObject(i); - Integer height = jsUnspent.getInt("height"); - String hash = jsUnspent.getString("tx_hash"); - if (height != -1) { - RequestWalletInfoTask task = new RequestWalletInfoTask(request.Host, request.Port); - requestTasks.add(task); - task.execute(Electrum_Request.GetHeader(mWalletAddress, String.valueOf(height)), - Electrum_Request.GetTransaction(mWalletAddress, hash)); - } - } - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - engine.SwitchNode(null); - } - } else if (request.isMethod(Electrum_Request.METHOD_GetHistory)) { - try { - String mWalletAddress = request.getParams().getString(0); - mCardListAdapter.UpdateWalletHistory(mWalletAddress, request.getResultArray()); - - JSONArray historyList = request.getResultArray(); - - for (int i = 0; i < historyList.length(); i++) { - JSONObject jsUnspent = historyList.getJSONObject(i); - Integer height = jsUnspent.getInt("height"); - String hash = jsUnspent.getString("tx_hash"); - if (height != -1) { - RequestWalletInfoTask task = new RequestWalletInfoTask(request.Host, request.Port); - requestTasks.add(task); - - task.execute(Electrum_Request.GetHeader(mWalletAddress, String.valueOf(height)), - Electrum_Request.GetTransaction(mWalletAddress, hash)); - } - } - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - engine.SwitchNode(null); - } - } else if (request.isMethod(Electrum_Request.METHOD_GetHeader)) { - try { - String mWalletAddress = request.WalletAddress; - - mCardListAdapter.UpdateWalletHeader(mWalletAddress, request.getResult()); - - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - engine.SwitchNode(null); - } - } else if (request.isMethod(Electrum_Request.METHOD_GetTransaction)) { - try { - Log.e("MainActivityFragment_TX", request.TxHash); - String mWalletAddress = request.WalletAddress; - String tx = request.TxHash; - String raw = request.getResultString(); - Log.e("MainActivityFragment_R", raw); - mCardListAdapter.UpdateTransaction(mWalletAddress, tx, raw); - - } catch (JSONException e) { - e.printStackTrace(); - mCardListAdapter.UpdateWalletError(request.WalletAddress, "Cannot obtain data from blockchain"); - FinishWithError(request.WalletAddress, e.toString()); - engine.SwitchNode(null); - } - } - } else { - if (sharedCounter != null) { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if (errCounter >= sharedCounter.allRequest) { - FinishWithError(request.WalletAddress, request.error); - engine.SwitchNode(null); - } - } else { - FinishWithError(request.WalletAddress, request.error); - engine.SwitchNode(null); - } - - } - } catch (JSONException e) { - if (sharedCounter != null) { - int errCounter = sharedCounter.errorRequest.incrementAndGet(); - if (errCounter >= sharedCounter.allRequest) { - e.printStackTrace(); - mCardListAdapter.UpdateWalletError(request.WalletAddress, "Cannot obtain data from blockchain"); - engine.SwitchNode(null); - } - } else { - e.printStackTrace(); - mCardListAdapter.UpdateWalletError(request.WalletAddress, "Cannot obtain data from blockchain"); - engine.SwitchNode(null); - } - - } - } - - if ( mSwipeRefreshLayout!=null && requestTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); //TODO: tmp - } - - } - - private class ETHRequestTask extends Infura_Task { - ETHRequestTask(Blockchain blockchain) { - super(blockchain); - } - - void FinishWithError(String wallet, String message) { - mCardListAdapter.UpdateWalletError(wallet, "Cannot obtain data from blockchain"); - } - - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - for (Infura_Request request : requests) { - try { - if (request.error == null) { - - if (request.isMethod(Infura_Request.METHOD_ETH_GetBalance)) { - try { - String mWalletAddress = request.getParams().getString(0); - - String balanceCap = request.getResultString(); - balanceCap = balanceCap.substring(2); - BigInteger l = new BigInteger(balanceCap, 16); - BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); - Long balance = d.longValue(); - if (request.getBlockchain() != Blockchain.Token) - mCardListAdapter.UpdateWalletBalance(mWalletAddress, balance, l.toString(10), getValidationNodeDescription()); - mCardListAdapter.UpdateWalletBalanceOnlyAlter(mWalletAddress, l.toString(10)); - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - } - } else if (request.isMethod(Infura_Request.METHOD_ETH_Call)) { - try { - String mWalletAddress = request.WalletAddress; - - String balanceCap = request.getResultString(); - balanceCap = balanceCap.substring(2); - BigInteger l = new BigInteger(balanceCap, 16); - Long balance = l.longValue(); - if (l.compareTo(BigInteger.ZERO) == 0) { - mCardListAdapter.UpdateWalletBlockchain(mWalletAddress, Blockchain.Ethereum); - mCardListAdapter.AddWalletBlockchainNameToken(mWalletAddress); - Tangem_Card card = mCardListAdapter.getCardByWallet(request.WalletAddress); - if (card != null) { - refreshCard(card); - } - return; - } - mCardListAdapter.UpdateWalletBalance(mWalletAddress, balance, l.toString(10), getValidationNodeDescription()); - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - } catch (Exception e) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - } - } else if (request.isMethod(Infura_Request.METHOD_ETH_GetOutTransactionCount)) { - try { - String mWalletAddress = request.getParams().getString(0); - - String nonce = request.getResultString(); - nonce = nonce.substring(2); - BigInteger count = new BigInteger(nonce, 16); - - mCardListAdapter.UpdateWalletCoutConfirmTx(mWalletAddress, count); - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - } - } - } else { - FinishWithError(request.WalletAddress, request.error); - } - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(request.WalletAddress, e.toString()); - } - } - } - - } - - private class RateInfoTask extends ExchangeTask { - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - for (ExchangeRequest request : requests) { - if (request.error == null) { - try { - - JSONArray arr = request.getAnswerList(); - for (int i = 0; i < arr.length(); ++i) { - JSONObject obj = arr.getJSONObject(i); - String currency = obj.getString("id"); - - boolean stop = false; - boolean stopAlter = false; - if (currency.equals(request.currency)) { - String usd = obj.getString("price_usd"); - - Float rate = Float.valueOf(usd); - mCardListAdapter.UpdateRate(request.WalletAddress, rate); - stop = true; - } - - if (currency.equals(request.currencyAlter)) { - String usd = obj.getString("price_usd"); - - Float rate = Float.valueOf(usd); - mCardListAdapter.UpdateRateAlter(request.WalletAddress, rate); - stopAlter = true; - } - - if (stop && stopAlter) { - break; - } - } - - - //mCardListAdapter.UpdateWalletBalance(mWalletAddress, balance, l.toString(10)); - } catch (JSONException e) { - e.printStackTrace(); - } - } - } - } - } - - -} +package com.tangem.wallet; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import android.widget.Toast; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.NfcManager; +import com.tangem.cardReader.Util; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + + +/** + * A placeholder fragment containing a simple view. + */ +public class MainActivityFragment extends Fragment implements NfcAdapter.ReaderCallback, CardListAdapter.UiCallbacks, CardProtocol.Notifications, MainActivity.OnCardsClean { + + private static final int REQUEST_CODE_SHOW_CARD_ACTIVITY = 1; + private static final int REQUEST_CODE_ENTER_PIN_ACTIVITY = 2; + private static final int REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS = 3; + private NfcManager mNfcManager; + private ArrayList slCardUIDs = new ArrayList<>(); + private static final String logTag = "MainActivityFragment"; + private ProgressBar progressBar; + + private CardListAdapter mCardListAdapter; + + public CardListAdapter getCardListAdapter() { + return mCardListAdapter; + } + + private ReadCardInfoTask readCardInfoTask; + private SwipeRefreshLayout mSwipeRefreshLayout; //TODO: tmp + List requestTasks = new ArrayList<>(); + + public MainActivityFragment() { + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mCardListAdapter.onSaveInstanceState(outState); + outState.putStringArrayList("slCardUIDs", slCardUIDs); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View result = inflater.inflate(R.layout.fragment_main, container, false); + + mNfcManager = new NfcManager(this.getActivity(), this); + verifyPermissions(); + + progressBar = result.findViewById(R.id.progressBar); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + RecyclerView rvCards = result.findViewById(R.id.lvCards); + rvCards.setLayoutManager(new LinearLayoutManager(getContext())); + mCardListAdapter = new CardListAdapter(getActivity().getLayoutInflater(), savedInstanceState, this); + rvCards.setAdapter(mCardListAdapter); + if (savedInstanceState != null && savedInstanceState.containsKey("slCardUIDs")) { + slCardUIDs = savedInstanceState.getStringArrayList("slCardUIDs"); + } + + // SwipeRefreshLayout + mSwipeRefreshLayout = result.findViewById(R.id.swipe_container); //TODO: tmp + + if( mSwipeRefreshLayout!=null ) { + SwipeRefreshLayout.OnRefreshListener onRefreshListener = new SwipeRefreshLayout.OnRefreshListener() { + + @Override + public void onRefresh() { + //Update + // Showing refresh animation before making http call + + if (requestTasks.size() > 0) return; + + if (requestTasks.size() > 0) { //TODO: tmp + mSwipeRefreshLayout.setRefreshing(true); + } else { + mSwipeRefreshLayout.setRefreshing(false); + } + } + }; + mSwipeRefreshLayout.setOnRefreshListener(onRefreshListener); //TODO: tmp + } + + ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + return false; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { + //Remove swiped item from list and notify the RecyclerView + 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 = ""; + } + mCardListAdapter.removeCard(cardIndex); + if (mCardListAdapter.getItemCount() == 0 && getActivity().getClass() == MainActivity.class) { + ((MainActivity) getActivity()).hideCleanButton(); + } + } + }); + + itemTouchHelper.attachToRecyclerView(rvCards); + + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).setOnCardsClean(this); +// ((MainActivity) getActivity()).setOnCreateNFCDialog(this); + ((MainActivity) getActivity()).setNfcAdapterReaderCallback(this); + } + + return result; + } + + private void verifyPermissions() { + NfcManager.verifyPermissions(getActivity()); + if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + Log.e("QRScanActivity", "User hasn't granted permission to use camera"); + ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS); + } + } + + int unsuccessReadCount = 0; + Tag lastTag = null; + + @Override + public void onTagDiscovered(Tag tag) { + try { + // get IsoDep handle and run cardReader thread + final IsoDep isoDep = IsoDep.get(tag); + if (isoDep == null) { + throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); + } + + byte UID[] = tag.getId(); + String sUID = Util.byteArrayToHexString(UID); + if (slCardUIDs.indexOf(sUID) != -1) { + Log.d(logTag, "Repeat UID: " + sUID); + mNfcManager.IgnoreTag(isoDep.getTag()); + return; + } else { + Log.v(logTag, "UID: " + sUID); + } + +// Log.e(logTag,"setTimeout("+String.valueOf(1000 + 3000 * unsuccessReadCount)+")"); + if (unsuccessReadCount < 2) { + isoDep.setTimeout(2000 + 5000 * unsuccessReadCount); + } else { + isoDep.setTimeout(90000); + } + lastTag = tag; + + readCardInfoTask = new ReadCardInfoTask(isoDep, this); + readCardInfoTask.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void onResume() { + super.onResume(); + + mNfcManager.onResume(); + } + + @Override + public void onPause() { + mNfcManager.onPause(); + if (readCardInfoTask != null) { + readCardInfoTask.cancel(true); + } + super.onPause(); + } + + @Override + public void onStop() { + // dismiss enable NFC dialog + mNfcManager.onStop(); + if (readCardInfoTask != null) { + readCardInfoTask.cancel(true); + } + for (RequestWalletInfoTask rt : requestTasks) { + rt.cancel(true); + } + super.onStop(); + } + + public void refreshCard(Tangem_Card card) { + CoinEngine engine = CoinEngineFactory.Create(card.getBlockchain()); + if (card.getBlockchain() == Blockchain.Ethereum || card.getBlockchain() == Blockchain.EthereumTestNet) { + ETHRequestTask task = new ETHRequestTask(card.getBlockchain()); + Infura_Request req = Infura_Request.GetBalance(card.getWallet()); + req.setID(67); + req.setBlockchain(card.getBlockchain()); + Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(card.getWallet()); + reqNonce.setID(67); + reqNonce.setBlockchain(card.getBlockchain()); + + task.execute(req, reqNonce); + + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "ethereum", "ethereum"); + taskRate.execute(rate); + + } else if (card.getBlockchain() == Blockchain.BitcoinTestNet || card.getBlockchain() == Blockchain.Bitcoin) { + SharedData data = new SharedData(SharedData.COUNT_REQUEST); + for (int i = 0; i < data.allRequest; ++i) { + + String nodeAddress = engine.GetNextNode(card); + int nodePort = engine.GetNextNodePort(card); + RequestWalletInfoTask connectTaskEx = new RequestWalletInfoTask(nodeAddress, nodePort, data); + connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(card.getWallet())); + } + + String nodeAddress = engine.GetNode(card); + int nodePort = engine.GetNodePort(card); + RequestWalletInfoTask task = new RequestWalletInfoTask(nodeAddress, nodePort); + if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(true); //TODO: tmp + requestTasks.add(task); + task.execute(Electrum_Request.ListUnspent(card.getWallet()), Electrum_Request.ListHistory(card.getWallet())); + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "bitcoin", "bitcoin"); + taskRate.execute(rate); + + } + else if (card.getBlockchain() == Blockchain.BitcoinCashTestNet || card.getBlockchain() == Blockchain.BitcoinCash) { + SharedData data = new SharedData(SharedData.COUNT_REQUEST); + for (int i = 0; i < data.allRequest; ++i) { + + String nodeAddress = engine.GetNextNode(card); + int nodePort = engine.GetNextNodePort(card); + RequestWalletInfoTask connectTaskEx = new RequestWalletInfoTask(nodeAddress, nodePort, data); + connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(card.getWallet())); + } + + String nodeAddress = engine.GetNode(card); + int nodePort = engine.GetNodePort(card); + RequestWalletInfoTask task = new RequestWalletInfoTask(nodeAddress, nodePort); + if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(true); //TODO: tmp + requestTasks.add(task); + task.execute(Electrum_Request.ListUnspent(card.getWallet()), Electrum_Request.ListHistory(card.getWallet())); + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "bitcoin-cash", "bitcoin-cash"); + taskRate.execute(rate); + + }else if (card.getBlockchain() == Blockchain.Token) { + ETHRequestTask updateETH = new ETHRequestTask(card.getBlockchain()); + Infura_Request reqETH = Infura_Request.GetTokenBalance(card.getWallet(), engine.GetContractAddress(card), engine.GetTokenDecimals(card)); + reqETH.setID(67); + reqETH.setBlockchain(card.getBlockchain()); + + + Infura_Request reqBalance = Infura_Request.GetBalance(card.getWallet()); + reqBalance.setID(67); + reqBalance.setBlockchain(card.getBlockchain()); + + + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "basic-attention-token", "ethereum"); + taskRate.execute(rate); + + Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(card.getWallet()); + reqNonce.setID(67); + reqNonce.setBlockchain(card.getBlockchain()); + + updateETH.execute(reqETH, reqNonce, reqBalance); + } + } + + public void addCard(final Tangem_Card card) { + if (mCardListAdapter != null) { + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + unsuccessReadCount = 0; + slCardUIDs.add(0, card.getUID()); + mCardListAdapter.addCard(card); + + CoinEngine engine = CoinEngineFactory.Create(card.getBlockchain()); + + if (card.getStatus() == Tangem_Card.Status.Loaded) { + + if (card.getBlockchain() == Blockchain.Ethereum || card.getBlockchain() == Blockchain.EthereumTestNet) { + ETHRequestTask task = new ETHRequestTask(card.getBlockchain()); + Infura_Request req = Infura_Request.GetBalance(card.getWallet()); + req.setID(67); + req.setBlockchain(card.getBlockchain()); + Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(card.getWallet()); + reqNonce.setID(67); + reqNonce.setBlockchain(card.getBlockchain()); + + task.execute(req, reqNonce); + + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "ethereum", "ethereum"); + taskRate.execute(rate); + + } else if (card.getBlockchain() == Blockchain.BitcoinTestNet || card.getBlockchain() == Blockchain.Bitcoin) { + SharedData data = new SharedData(SharedData.COUNT_REQUEST); + + for (int i = 0; i < data.allRequest; ++i) { + String nodeAddress = engine.GetNextNode(card); + int nodePort = engine.GetNextNodePort(card); + + RequestWalletInfoTask connectTaskEx = new RequestWalletInfoTask(nodeAddress, nodePort, data); + connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(card.getWallet())); + } + + String nodeAddress = engine.GetNode(card); + int nodePort = engine.GetNodePort(card); + + RequestWalletInfoTask task = new RequestWalletInfoTask(nodeAddress, nodePort); + if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(true); //TODO: tmp + + requestTasks.add(task); + task.execute(Electrum_Request.ListUnspent(card.getWallet()), Electrum_Request.ListHistory(card.getWallet())); + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "bitcoin", "bitcoin"); + taskRate.execute(rate); + + }else if (card.getBlockchain() == Blockchain.BitcoinCashTestNet || card.getBlockchain() == Blockchain.BitcoinCash) { + SharedData data = new SharedData(SharedData.COUNT_REQUEST); + + for (int i = 0; i < data.allRequest; ++i) { + String nodeAddress = engine.GetNextNode(card); + int nodePort = engine.GetNextNodePort(card); + + RequestWalletInfoTask connectTaskEx = new RequestWalletInfoTask(nodeAddress, nodePort, data); + connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(card.getWallet())); + } + + String nodeAddress = engine.GetNode(card); + int nodePort = engine.GetNodePort(card); + + RequestWalletInfoTask task = new RequestWalletInfoTask(nodeAddress, nodePort); + if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(true); //TODO: tmp + + requestTasks.add(task); + task.execute(Electrum_Request.ListUnspent(card.getWallet()), Electrum_Request.ListHistory(card.getWallet())); + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "bitcoin-cash", "bitcoin-cash"); + taskRate.execute(rate); + + } + else if (card.getBlockchain() == Blockchain.Token) { + ETHRequestTask updateETH = new ETHRequestTask(card.getBlockchain()); + Infura_Request reqETH = Infura_Request.GetTokenBalance(card.getWallet(), engine.GetContractAddress(card), engine.GetTokenDecimals(card)); + reqETH.setID(67); + reqETH.setBlockchain(card.getBlockchain()); + + + Infura_Request reqBalance = Infura_Request.GetBalance(card.getWallet()); + reqBalance.setID(67); + reqBalance.setBlockchain(card.getBlockchain()); + + + RateInfoTask taskRate = new RateInfoTask(); + ExchangeRequest rate = ExchangeRequest.GetRate(card.getWallet(), "basic-attention-token", "ethereum"); + taskRate.execute(rate); + + Infura_Request reqNonce = Infura_Request.GetOutTransactionCount(card.getWallet()); + reqNonce.setID(67); + reqNonce.setBlockchain(card.getBlockchain()); + + updateETH.execute(reqETH, reqNonce, reqBalance); + } + } + if (getActivity().getClass() == MainActivity.class) { + ((MainActivity) getActivity()).showCleanButton(); + } + } + }); + } + } + + @Override + public void onViewCard(Bundle cardInfo) { + + if( mSwipeRefreshLayout!=null ) mSwipeRefreshLayout.setRefreshing(false); //TODO: tmp + + String UID = cardInfo.getString("UID"); + Tangem_Card card = new Tangem_Card(UID); + card.LoadFromBundle(cardInfo.getBundle("Card")); + + + Intent intent; + + if (card.getStatus() == Tangem_Card.Status.Empty) { + intent = new Intent(getActivity(), EmptyWalletActivity.class); + } else if (card.getStatus() == Tangem_Card.Status.Loaded) { + intent = new Intent(getActivity(), LoadedWalletActivity.class); + } else if (card.getStatus() == Tangem_Card.Status.NotPersonalized || card.getStatus() == Tangem_Card.Status.Purged) { + return; + } else { + intent = new Intent(getActivity(), LoadedWalletActivity.class); + } + + intent.putExtras(cardInfo); + startActivityForResult(intent, REQUEST_CODE_SHOW_CARD_ACTIVITY); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + Log.d(logTag, "ActivityResult: requestCode = " + requestCode + ", resultCode = " + resultCode); + // если пришло ОК + if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_SHOW_CARD_ACTIVITY) { + if (data != null && data.getExtras().containsKey("UID")) { + final Tangem_Card card = new Tangem_Card(data.getStringExtra("UID")); + card.LoadFromBundle(data.getBundleExtra("Card")); + if (data.getStringExtra("modification").equals("delete")) { + mCardListAdapter.removeCard(card); + for (int i = 0; i < slCardUIDs.size(); i++) { + if (slCardUIDs.get(i).equals(card.getUID())) { + slCardUIDs.remove(i); + break; + } + } + if (mCardListAdapter.getItemCount() == 0 && getActivity().getClass() == MainActivity.class) { + ((MainActivity) getActivity()).hideCleanButton(); + } + } else if (data.getStringExtra("modification").equals("update")) { + mCardListAdapter.updateCard(card); + } else if (data.getStringExtra("modification").equals("updateAndViewCard")) { + mCardListAdapter.updateCard(card); + onViewCard(data.getExtras()); + } + + } + } else if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_ENTER_PIN_ACTIVITY) { + if (lastTag != null) onTagDiscovered(lastTag); + } + } + + @Override + public void doClean() { + mCardListAdapter.clearCards(); + slCardUIDs.clear(); + for (RequestWalletInfoTask rt : requestTasks) { + rt.cancel(true); + } + lastRead_UID = ""; + } + + public void doEnterPIN() { + Intent intent = new Intent(getContext(), RequestPINActivity.class); + intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN.toString()); + startActivityForResult(intent, REQUEST_CODE_ENTER_PIN_ACTIVITY); + } + + private static String lastRead_UID = ""; + private static ArrayList lastRead_UnsuccessfullPINs = new ArrayList<>(); + private static Tangem_Card.EncryptionMode lastRead_Encryption = null; + + private class ReadCardInfoTask extends Thread { + + + IsoDep mIsoDep; + CardProtocol.Notifications mNotifications; + private boolean isCancelled = false; + + ReadCardInfoTask(IsoDep isoDep, CardProtocol.Notifications notifications) { + mIsoDep = isoDep; + mNotifications = notifications; + } + + @Override + public void run() { + if (mIsoDep == null) { + return; + } + try { + // for Samsung's bugs - + // Workaround for the Samsung Galaxy S5 (since the + // first connection always hangs on transceive). + int timeout = mIsoDep.getTimeout(); + mIsoDep.connect(); + mIsoDep.close(); + mIsoDep.connect(); + mIsoDep.setTimeout(timeout); + try { + CardProtocol protocol = new CardProtocol(getContext(), mIsoDep, mNotifications); + mNotifications.OnReadStart(protocol); + try { + mNotifications.OnReadProgress(protocol, 5); + + byte[] UID = mIsoDep.getTag().getId(); + String sUID = Util.byteArrayToHexString(UID); + if (!lastRead_UID.equals(sUID)) { + lastRead_UID = sUID; + lastRead_UnsuccessfullPINs.clear(); + lastRead_Encryption = null; + } + + Log.i("ReadCardInfoTask", "[-- Start read card info --]"); + + if (isCancelled) return; + + protocol.setPIN(PINStorage.getDefaultPIN()); + protocol.clearReadResult(); + + if (lastRead_Encryption == null) { + Log.i("ReadCardInfoTask", "Try get supported encryption mode"); + protocol.run_GetSupportedEncryption(); + } else { + Log.i("ReadCardInfoTask", "Use already defined encryption mode: " + lastRead_Encryption.name()); + protocol.getCard().encryptionMode = lastRead_Encryption; + } + + if (protocol.haveReadResult()) { + //already have read result (obtained while get supported encryption), only read issuer data and define offline balance + protocol.parseReadResult(); + protocol.run_ReadOrWriteIssuerDataAndDefineOfflineBalance(); + mNotifications.OnReadProgress(protocol, 60); + PINStorage.setLastUsedPIN(protocol.getCard().getPIN()); + } else { + //don't have read result - may be don't get supported encryption on this try, need encryption or need another PIN + if (lastRead_Encryption == null) { + // we try get supported encryption on this time + lastRead_Encryption = protocol.getCard().encryptionMode; + if (protocol.getCard().encryptionMode == Tangem_Card.EncryptionMode.None) { + // default pin not accepted + lastRead_UnsuccessfullPINs.add(PINStorage.getDefaultPIN()); + } + } + + boolean pinFound = false; + for (String PIN : PINStorage.getPINs()) { + Log.e("ReadCardInfoTask", "PIN: " + PIN); + + boolean skipPin = false; + for (int i = 0; i < lastRead_UnsuccessfullPINs.size(); i++) { + if (lastRead_UnsuccessfullPINs.get(i).equals(PIN)) { + skipPin = true; + break; + } + } + + if (skipPin) { + Log.e("ReadCardInfoTask", "Skip PIN - already checked before"); + continue; + } + + try { + protocol.setPIN(PIN); + if (protocol.getCard().encryptionMode != Tangem_Card.EncryptionMode.None) { + protocol.CreateProtocolKey(); + } + protocol.run_Read(); + mNotifications.OnReadProgress(protocol, 60); + PINStorage.setLastUsedPIN(PIN); + pinFound = true; + protocol.getCard().setPIN(PIN); + break; + } catch (CardProtocol.TangemException_InvalidPIN e) { + Log.e(logTag, e.getMessage()); + lastRead_UnsuccessfullPINs.add(PIN); + } + } + if (!pinFound) { + throw new CardProtocol.TangemException_InvalidPIN("No valid PIN found!"); + } + } + + protocol.run_CheckPIN2isDefault(); + + } catch (Exception e) { + e.printStackTrace(); + protocol.setError(e); + + } finally { + Log.i("ReadCardInfoTask", "[-- Finish read card info --]"); + mNotifications.OnReadFinish(protocol); + } + } finally { + mNfcManager.IgnoreTag(mIsoDep.getTag()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + void cancel(Boolean AllowInterrupt) { + try { + if (this.isAlive()) { + isCancelled = true; + join(500); + } + if (this.isAlive() && AllowInterrupt) { + interrupt(); + mNotifications.OnReadCancel(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + public void OnReadStart(CardProtocol cardProtocol) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setVisibility(View.VISIBLE); + progressBar.setProgress(5); + } + }); + } + + public void OnReadFinish(final CardProtocol cardProtocol) { + + readCardInfoTask = null; + + if (cardProtocol != null) { + if (cardProtocol.getError() == null) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); + addCard(cardProtocol.getCard()); + } + }); + } else { + // remove last UIDs because of error and no card read + progressBar.post(new Runnable() { + @Override + public void run() { + Toast.makeText(getContext(), "Try to scan again", Toast.LENGTH_LONG).show(); + unsuccessReadCount++; + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + 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(getActivity().getFragmentManager(), "NoExtendedLengthSupportDialog"); + } + } else { + lastTag = null; + } + } + }); + } + } + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + public void OnReadProgress(CardProtocol protocol, final int progress) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(progress); + } + }); + } + + public void OnReadCancel() { + + readCardInfoTask = null; + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + + public void OnReadWait(final int msec) { + WaitSecurityDelayDialog.OnReadWait(getActivity(), msec); + } + + @Override + public void OnReadBeforeRequest(int timeout) { + WaitSecurityDelayDialog.onReadBeforeRequest(getActivity(), timeout); + } + + @Override + public void OnReadAfterRequest() { + WaitSecurityDelayDialog.onReadAfterRequest(getActivity()); + } + + private class RequestWalletInfoTask extends Electrum_Task { + public RequestWalletInfoTask(String host, int port) { + super(host, port); + } + + + public RequestWalletInfoTask(String host, int port, SharedData sharedData) { + super(host, port, sharedData); + } + + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + } + + @Override + protected void onCancelled() { + super.onCancelled(); + requestTasks.remove(this); + if ( mSwipeRefreshLayout!=null && requestTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); //TODO: tmp + + } + + void FinishWithError(String wallet, String message) { + mCardListAdapter.UpdateWalletError(wallet, "Cannot obtain data from blockchain"); + } + + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + requestTasks.remove(this); + Log.i("RequestWalletInfoTask", "onPostExecute[" + String.valueOf(requests.size()) + "]"); + + CoinEngine engine = CoinEngineFactory.Create(Blockchain.Bitcoin); + + for (Electrum_Request request : requests) { + try { + + if (request.error == null) { + if (request.isMethod(Electrum_Request.METHOD_GetBalance)) { + try { + Long conf = request.getResult().getLong("confirmed"); + Long unconf = request.getResult().getLong("unconfirmed"); + if (sharedCounter != null) { + int counter = sharedCounter.requestCounter.incrementAndGet(); + if (counter != 1) { + continue; + } + } + String mWalletAddress = request.getParams().getString(0); + mCardListAdapter.UpdateWalletBalance(mWalletAddress, conf, unconf, getValidationNodeDescription()); + } catch (JSONException e) { + if (sharedCounter != null) { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if (errCounter == sharedCounter.allRequest) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + engine.SwitchNode(null); + } + } else { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + engine.SwitchNode(null); + } + } + } else if (request.isMethod(Electrum_Request.METHOD_ListUnspent)) { + try { + String mWalletAddress = request.getParams().getString(0); + mCardListAdapter.UpdateWalletUnspent(mWalletAddress, request.getResultArray()); + + JSONArray unspentList = request.getResultArray(); + + for (int i = 0; i < unspentList.length(); i++) { + JSONObject jsUnspent = unspentList.getJSONObject(i); + Integer height = jsUnspent.getInt("height"); + String hash = jsUnspent.getString("tx_hash"); + if (height != -1) { + RequestWalletInfoTask task = new RequestWalletInfoTask(request.Host, request.Port); + requestTasks.add(task); + task.execute(Electrum_Request.GetHeader(mWalletAddress, String.valueOf(height)), + Electrum_Request.GetTransaction(mWalletAddress, hash)); + } + } + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + engine.SwitchNode(null); + } + } else if (request.isMethod(Electrum_Request.METHOD_GetHistory)) { + try { + String mWalletAddress = request.getParams().getString(0); + mCardListAdapter.UpdateWalletHistory(mWalletAddress, request.getResultArray()); + + JSONArray historyList = request.getResultArray(); + + for (int i = 0; i < historyList.length(); i++) { + JSONObject jsUnspent = historyList.getJSONObject(i); + Integer height = jsUnspent.getInt("height"); + String hash = jsUnspent.getString("tx_hash"); + if (height != -1) { + RequestWalletInfoTask task = new RequestWalletInfoTask(request.Host, request.Port); + requestTasks.add(task); + + task.execute(Electrum_Request.GetHeader(mWalletAddress, String.valueOf(height)), + Electrum_Request.GetTransaction(mWalletAddress, hash)); + } + } + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + engine.SwitchNode(null); + } + } else if (request.isMethod(Electrum_Request.METHOD_GetHeader)) { + try { + String mWalletAddress = request.WalletAddress; + + mCardListAdapter.UpdateWalletHeader(mWalletAddress, request.getResult()); + + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + engine.SwitchNode(null); + } + } else if (request.isMethod(Electrum_Request.METHOD_GetTransaction)) { + try { + Log.e("MainActivityFragment_TX", request.TxHash); + String mWalletAddress = request.WalletAddress; + String tx = request.TxHash; + String raw = request.getResultString(); + Log.e("MainActivityFragment_R", raw); + mCardListAdapter.UpdateTransaction(mWalletAddress, tx, raw); + + } catch (JSONException e) { + e.printStackTrace(); + mCardListAdapter.UpdateWalletError(request.WalletAddress, "Cannot obtain data from blockchain"); + FinishWithError(request.WalletAddress, e.toString()); + engine.SwitchNode(null); + } + } + } else { + if (sharedCounter != null) { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if (errCounter >= sharedCounter.allRequest) { + FinishWithError(request.WalletAddress, request.error); + engine.SwitchNode(null); + } + } else { + FinishWithError(request.WalletAddress, request.error); + engine.SwitchNode(null); + } + + } + } catch (JSONException e) { + if (sharedCounter != null) { + int errCounter = sharedCounter.errorRequest.incrementAndGet(); + if (errCounter >= sharedCounter.allRequest) { + e.printStackTrace(); + mCardListAdapter.UpdateWalletError(request.WalletAddress, "Cannot obtain data from blockchain"); + engine.SwitchNode(null); + } + } else { + e.printStackTrace(); + mCardListAdapter.UpdateWalletError(request.WalletAddress, "Cannot obtain data from blockchain"); + engine.SwitchNode(null); + } + + } + } + + if ( mSwipeRefreshLayout!=null && requestTasks.size() == 0) mSwipeRefreshLayout.setRefreshing(false); //TODO: tmp + } + + } + + private class ETHRequestTask extends Infura_Task { + ETHRequestTask(Blockchain blockchain) { + super(blockchain); + } + + void FinishWithError(String wallet, String message) { + mCardListAdapter.UpdateWalletError(wallet, "Cannot obtain data from blockchain"); + } + + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + for (Infura_Request request : requests) { + try { + if (request.error == null) { + + if (request.isMethod(Infura_Request.METHOD_ETH_GetBalance)) { + try { + String mWalletAddress = request.getParams().getString(0); + + String balanceCap = request.getResultString(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + BigInteger d = l.divide(new BigInteger("1000000000000000000", 10)); + Long balance = d.longValue(); + if (request.getBlockchain() != Blockchain.Token) + mCardListAdapter.UpdateWalletBalance(mWalletAddress, balance, l.toString(10), getValidationNodeDescription()); + mCardListAdapter.UpdateWalletBalanceOnlyAlter(mWalletAddress, l.toString(10)); + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + } + } else if (request.isMethod(Infura_Request.METHOD_ETH_Call)) { + try { + String mWalletAddress = request.WalletAddress; + + String balanceCap = request.getResultString(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + Long balance = l.longValue(); + if (l.compareTo(BigInteger.ZERO) == 0) { + mCardListAdapter.UpdateWalletBlockchain(mWalletAddress, Blockchain.Ethereum); + mCardListAdapter.AddWalletBlockchainNameToken(mWalletAddress); + Tangem_Card card = mCardListAdapter.getCardByWallet(request.WalletAddress); + if (card != null) { + refreshCard(card); + } + return; + } + mCardListAdapter.UpdateWalletBalance(mWalletAddress, balance, l.toString(10), getValidationNodeDescription()); + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + } catch (Exception e) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + } + } else if (request.isMethod(Infura_Request.METHOD_ETH_GetOutTransactionCount)) { + try { + String mWalletAddress = request.getParams().getString(0); + + String nonce = request.getResultString(); + nonce = nonce.substring(2); + BigInteger count = new BigInteger(nonce, 16); + + mCardListAdapter.UpdateWalletCoutConfirmTx(mWalletAddress, count); + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + } + } + } else { + FinishWithError(request.WalletAddress, request.error); + } + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(request.WalletAddress, e.toString()); + } + } + } + + } + + private class RateInfoTask extends ExchangeTask { + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + for (ExchangeRequest request : requests) { + if (request.error == null) { + try { + + JSONArray arr = request.getAnswerList(); + for (int i = 0; i < arr.length(); ++i) { + JSONObject obj = arr.getJSONObject(i); + String currency = obj.getString("id"); + + boolean stop = false; + boolean stopAlter = false; + if (currency.equals(request.currency)) { + String usd = obj.getString("price_usd"); + + Float rate = Float.valueOf(usd); + mCardListAdapter.UpdateRate(request.WalletAddress, rate); + stop = true; + } + + if (currency.equals(request.currencyAlter)) { + String usd = obj.getString("price_usd"); + + Float rate = Float.valueOf(usd); + mCardListAdapter.UpdateRateAlter(request.WalletAddress, rate); + stopAlter = true; + } + + if (stop && stopAlter) { + break; + } + } + + + //mCardListAdapter.UpdateWalletBalance(mWalletAddress, balance, l.toString(10)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + } + } + } + + +} diff --git a/app/src/main/java/com/tangem/wallet/Manufacturer.java b/app/src/main/java/com/tangem/wallet/Manufacturer.java index c904f9b308..c0db7d232c 100644 --- a/app/src/main/java/com/tangem/wallet/Manufacturer.java +++ b/app/src/main/java/com/tangem/wallet/Manufacturer.java @@ -1,126 +1,126 @@ -package com.tangem.wallet; - -import android.util.Log; - -import org.spongycastle.asn1.ASN1EncodableVector; -import org.spongycastle.asn1.ASN1Integer; -import org.spongycastle.asn1.DERSequence; -import org.spongycastle.jce.ECNamedCurveTable; -import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; -import org.spongycastle.jce.spec.ECPublicKeySpec; -import org.spongycastle.math.ec.ECPoint; - -import java.math.BigInteger; -import java.security.KeyFactory; -import java.security.PublicKey; -import java.util.Arrays; - -/** - * Created by dvol on 09.08.2017. - */ - -public enum Manufacturer { -// Unknown("", "Unknown", new byte[]{}), -// SMARTCASH_AG("SMART CASH AG","SMART CASH AG", -// new byte[]{0x04, -// (byte) 0x4F, (byte) 0x53, (byte) 0x90, (byte) 0x2D, (byte) 0x50, (byte) 0xE2, (byte) 0xBB, (byte) 0x16, -// (byte) 0xD3, (byte) 0xDD, (byte) 0xC7, (byte) 0xA2, (byte) 0x03, (byte) 0x97, (byte) 0x28, (byte) 0x5E, -// (byte) 0x94, (byte) 0x21, (byte) 0x53, (byte) 0x69, (byte) 0x59, (byte) 0x8C, (byte) 0xE4, (byte) 0xDD, -// (byte) 0x62, (byte) 0x42, (byte) 0xDD, (byte) 0xB4, (byte) 0x5B, (byte) 0x96, (byte) 0xA1, (byte) 0x03, -// (byte) 0x1A, (byte) 0xF5, (byte) 0xC9, (byte) 0x73, (byte) 0x94, (byte) 0xC6, (byte) 0xF9, (byte) 0xC8, -// (byte) 0xD7, (byte) 0x6F, (byte) 0x38, (byte) 0xF9, (byte) 0x65, (byte) 0xCB, (byte) 0xA8, (byte) 0xAE, -// (byte) 0x85, (byte) 0xAF, (byte) 0xF7, (byte) 0x68, (byte) 0x55, (byte) 0xDC, (byte) 0xAA, (byte) 0x08, -// (byte) 0xF3, (byte) 0xCD, (byte) 0x15, (byte) 0x43, (byte) 0x04, (byte) 0x19, (byte) 0xF4, (byte) 0x49}), -// DEVELOPERS_SMARTCASH_AG("DEVELOP CASH AG","SMART CASH AG (DEVELOPERS)", -// new byte[]{0x04, -// (byte) 0xBA, (byte) 0xB8, (byte) 0x6D, (byte) 0x56, (byte) 0x29, (byte) 0x8C, (byte) 0x99, (byte) 0x6F, -// (byte) 0x56, (byte) 0x4A, (byte) 0x84, (byte) 0xFC, (byte) 0x88, (byte) 0xE2, (byte) 0x8A, (byte) 0xED, -// (byte) 0x38, (byte) 0x18, (byte) 0x4B, (byte) 0x12, (byte) 0xF0, (byte) 0x7E, (byte) 0x51, (byte) 0x91, -// (byte) 0x13, (byte) 0xBE, (byte) 0xF4, (byte) 0x8C, (byte) 0x76, (byte) 0xF3, (byte) 0xDF, (byte) 0x3A, -// -// (byte) 0xDC, (byte) 0x30, (byte) 0x35, (byte) 0x99, (byte) 0xB0, (byte) 0x8A, (byte) 0xC0, (byte) 0x5B, -// (byte) 0x55, (byte) 0xEC, (byte) 0x3D, (byte) 0xF9, (byte) 0x8D, (byte) 0x93, (byte) 0x38, (byte) 0x57, -// (byte) 0x3A, (byte) 0x62, (byte) 0x42, (byte) 0xF7, (byte) 0x6F, (byte) 0x5D, (byte) 0x28, (byte) 0xF4, -// (byte) 0xF0, (byte) 0xF3, (byte) 0x64, (byte) 0xE8, (byte) 0x7E, (byte) 0x8F, (byte) 0xCA, (byte) 0x2F}); - - - Unknown("", "Unknown"), - SMARTCASH_AG("SMART CASH AG", "SMART CASH AG"), - DEVELOPERS_SMARTCASH_AG("DEVELOP CASH AG", "SMART CASH AG (DEVELOPERS)"), - SMARTCASH("SMART CASH", "SMART CASH"); - - private String ID; - private String officialName; -// private byte[] publicKey; - - // Manufacturer(String id, String officialName, byte[] publicKey) { - Manufacturer(String id, String officialName) { - this.ID = id; - this.officialName = officialName; -// this.publicKey = publicKey; - } - - public String getOfficialName() { - return officialName; - } - -// public boolean VerifySignature(byte[] Challenge, byte[] Salt, byte[] Signature) { -// -// if (publicKey == null || Challenge == null || Salt == null || Signature == null) { -// Log.e(getOfficialName(), "Not all data read, can't check signature!"); -// return false; -// } -// try { -// java.security.Signature signature = java.security.Signature.getInstance("SHA256withECDSA"); -// ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); -// KeyFactory factory = KeyFactory.getInstance("EC", "SC"); -// -// ECPoint p1 = spec.getCurve().decodePoint(publicKey); -// -// ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec); -// -// PublicKey publicKey = factory.generatePublic(keySpec); -// signature.initVerify(publicKey); -// signature.update(Challenge); -// signature.update(Salt); -// -// ASN1EncodableVector v = new ASN1EncodableVector(); -// int size = Signature.length / 2; -// v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(Signature, 0, size)))); -// v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(Signature, size, size * 2)))); -// byte[] sigDer = new DERSequence(v).getEncoded(); -// -// if (signature.verify(sigDer)) { -// Log.i(getOfficialName(), "Signature verification OK"); -// return true; -// } else { -// Log.e(getOfficialName(), "Signature verification failed"); -// } -// } -// catch (Exception e) -// { -// e.printStackTrace(); -// } -// return false; -// } -// -// public static Manufacturer FindManufacturer(String ID, byte[] Challenge, byte[] Salt, byte[] Signature) { -// Manufacturer[] manufacturers = Manufacturer.values(); -// for (int i = 1; i < manufacturers.length; i++) { -// if ( manufacturers[i].ID.equals(ID) && manufacturers[i].VerifySignature(Challenge, Salt, Signature)) { -// return manufacturers[i]; -// } -// } -// return Manufacturer.Unknown; -// } - - public static Manufacturer FindManufacturer(String ID) { - Manufacturer[] manufacturers = Manufacturer.values(); - for (int i = 1; i < manufacturers.length; i++) { - if (manufacturers[i].ID.equals(ID)) { - return manufacturers[i]; - } - } - return Manufacturer.Unknown; - } -} +package com.tangem.wallet; + +import android.util.Log; + +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequence; +import org.spongycastle.jce.ECNamedCurveTable; +import org.spongycastle.jce.spec.ECNamedCurveParameterSpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; +import org.spongycastle.math.ec.ECPoint; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.util.Arrays; + +/** + * Created by dvol on 09.08.2017. + */ + +public enum Manufacturer { +// Unknown("", "Unknown", new byte[]{}), +// SMARTCASH_AG("SMART CASH AG","SMART CASH AG", +// new byte[]{0x04, +// (byte) 0x4F, (byte) 0x53, (byte) 0x90, (byte) 0x2D, (byte) 0x50, (byte) 0xE2, (byte) 0xBB, (byte) 0x16, +// (byte) 0xD3, (byte) 0xDD, (byte) 0xC7, (byte) 0xA2, (byte) 0x03, (byte) 0x97, (byte) 0x28, (byte) 0x5E, +// (byte) 0x94, (byte) 0x21, (byte) 0x53, (byte) 0x69, (byte) 0x59, (byte) 0x8C, (byte) 0xE4, (byte) 0xDD, +// (byte) 0x62, (byte) 0x42, (byte) 0xDD, (byte) 0xB4, (byte) 0x5B, (byte) 0x96, (byte) 0xA1, (byte) 0x03, +// (byte) 0x1A, (byte) 0xF5, (byte) 0xC9, (byte) 0x73, (byte) 0x94, (byte) 0xC6, (byte) 0xF9, (byte) 0xC8, +// (byte) 0xD7, (byte) 0x6F, (byte) 0x38, (byte) 0xF9, (byte) 0x65, (byte) 0xCB, (byte) 0xA8, (byte) 0xAE, +// (byte) 0x85, (byte) 0xAF, (byte) 0xF7, (byte) 0x68, (byte) 0x55, (byte) 0xDC, (byte) 0xAA, (byte) 0x08, +// (byte) 0xF3, (byte) 0xCD, (byte) 0x15, (byte) 0x43, (byte) 0x04, (byte) 0x19, (byte) 0xF4, (byte) 0x49}), +// DEVELOPERS_SMARTCASH_AG("DEVELOP CASH AG","SMART CASH AG (DEVELOPERS)", +// new byte[]{0x04, +// (byte) 0xBA, (byte) 0xB8, (byte) 0x6D, (byte) 0x56, (byte) 0x29, (byte) 0x8C, (byte) 0x99, (byte) 0x6F, +// (byte) 0x56, (byte) 0x4A, (byte) 0x84, (byte) 0xFC, (byte) 0x88, (byte) 0xE2, (byte) 0x8A, (byte) 0xED, +// (byte) 0x38, (byte) 0x18, (byte) 0x4B, (byte) 0x12, (byte) 0xF0, (byte) 0x7E, (byte) 0x51, (byte) 0x91, +// (byte) 0x13, (byte) 0xBE, (byte) 0xF4, (byte) 0x8C, (byte) 0x76, (byte) 0xF3, (byte) 0xDF, (byte) 0x3A, +// +// (byte) 0xDC, (byte) 0x30, (byte) 0x35, (byte) 0x99, (byte) 0xB0, (byte) 0x8A, (byte) 0xC0, (byte) 0x5B, +// (byte) 0x55, (byte) 0xEC, (byte) 0x3D, (byte) 0xF9, (byte) 0x8D, (byte) 0x93, (byte) 0x38, (byte) 0x57, +// (byte) 0x3A, (byte) 0x62, (byte) 0x42, (byte) 0xF7, (byte) 0x6F, (byte) 0x5D, (byte) 0x28, (byte) 0xF4, +// (byte) 0xF0, (byte) 0xF3, (byte) 0x64, (byte) 0xE8, (byte) 0x7E, (byte) 0x8F, (byte) 0xCA, (byte) 0x2F}); + + + Unknown("", "Unknown"), + SMARTCASH_AG("SMART CASH AG", "SMART CASH AG"), + DEVELOPERS_SMARTCASH_AG("DEVELOP CASH AG", "SMART CASH AG (DEVELOPERS)"), + SMARTCASH("SMART CASH", "SMART CASH"); + + private String ID; + private String officialName; +// private byte[] publicKey; + + // Manufacturer(String id, String officialName, byte[] publicKey) { + Manufacturer(String id, String officialName) { + this.ID = id; + this.officialName = officialName; +// this.publicKey = publicKey; + } + + public String getOfficialName() { + return officialName; + } + +// public boolean VerifySignature(byte[] Challenge, byte[] Salt, byte[] Signature) { +// +// if (publicKey == null || Challenge == null || Salt == null || Signature == null) { +// Log.e(getOfficialName(), "Not all data read, can't check signature!"); +// return false; +// } +// try { +// java.security.Signature signature = java.security.Signature.getInstance("SHA256withECDSA"); +// ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); +// KeyFactory factory = KeyFactory.getInstance("EC", "SC"); +// +// ECPoint p1 = spec.getCurve().decodePoint(publicKey); +// +// ECPublicKeySpec keySpec = new ECPublicKeySpec(p1, spec); +// +// PublicKey publicKey = factory.generatePublic(keySpec); +// signature.initVerify(publicKey); +// signature.update(Challenge); +// signature.update(Salt); +// +// ASN1EncodableVector v = new ASN1EncodableVector(); +// int size = Signature.length / 2; +// v.add(/*r*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(Signature, 0, size)))); +// v.add(/*s*/new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(Signature, size, size * 2)))); +// byte[] sigDer = new DERSequence(v).getEncoded(); +// +// if (signature.verify(sigDer)) { +// Log.i(getOfficialName(), "Signature verification OK"); +// return true; +// } else { +// Log.e(getOfficialName(), "Signature verification failed"); +// } +// } +// catch (Exception e) +// { +// e.printStackTrace(); +// } +// return false; +// } +// +// public static Manufacturer FindManufacturer(String ID, byte[] Challenge, byte[] Salt, byte[] Signature) { +// Manufacturer[] manufacturers = Manufacturer.values(); +// for (int i = 1; i < manufacturers.length; i++) { +// if ( manufacturers[i].ID.equals(ID) && manufacturers[i].VerifySignature(Challenge, Salt, Signature)) { +// return manufacturers[i]; +// } +// } +// return Manufacturer.Unknown; +// } + + public static Manufacturer FindManufacturer(String ID) { + Manufacturer[] manufacturers = Manufacturer.values(); + for (int i = 1; i < manufacturers.length; i++) { + if (manufacturers[i].ID.equals(ID)) { + return manufacturers[i]; + } + } + return Manufacturer.Unknown; + } +} diff --git a/app/src/main/java/com/tangem/wallet/NoExtendedLengthSupportDialog.java b/app/src/main/java/com/tangem/wallet/NoExtendedLengthSupportDialog.java index d5217286e0..a3c9337086 100644 --- a/app/src/main/java/com/tangem/wallet/NoExtendedLengthSupportDialog.java +++ b/app/src/main/java/com/tangem/wallet/NoExtendedLengthSupportDialog.java @@ -1,34 +1,34 @@ -package com.tangem.wallet; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; - -public class NoExtendedLengthSupportDialog extends DialogFragment { - - public static boolean allreadyShowed=false; - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - return new AlertDialog.Builder(getActivity()) - .setIcon(R.drawable.tangem_logo_small_new) - .setTitle("Warning") - .setMessage("The NFC adapter of the device does not support extended length APDU, it's possible that some functions will not work!") - .setPositiveButton("Got it", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - NoExtendedLengthSupportDialog.allreadyShowed=true; - } - } - ) - .create(); - } - - @Override - public void onCancel(DialogInterface dialog) { - super.onCancel(dialog); - } -} +package com.tangem.wallet; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; + +public class NoExtendedLengthSupportDialog extends DialogFragment { + + public static boolean allreadyShowed=false; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + return new AlertDialog.Builder(getActivity()) + .setIcon(R.drawable.tangem_logo_small_new) + .setTitle("Warning") + .setMessage("The NFC adapter of the device does not support extended length APDU, it's possible that some functions will not work!") + .setPositiveButton("Got it", + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + NoExtendedLengthSupportDialog.allreadyShowed=true; + } + } + ) + .create(); + } + + @Override + public void onCancel(DialogInterface dialog) { + super.onCancel(dialog); + } +} diff --git a/app/src/main/java/com/tangem/wallet/PINStorage.java b/app/src/main/java/com/tangem/wallet/PINStorage.java index b1e376d3aa..f16194d5fb 100644 --- a/app/src/main/java/com/tangem/wallet/PINStorage.java +++ b/app/src/main/java/com/tangem/wallet/PINStorage.java @@ -1,206 +1,206 @@ -package com.tangem.wallet; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.util.Base64; -import android.util.Log; - -import com.tangem.cardReader.CardProtocol; - -import java.util.ArrayList; -import java.util.List; - -import javax.crypto.Cipher; - -/** - * Created by dvol on 12.09.2017. - * Global PIN Storage - */ - -class PINStorage { - private static String mSavedPIN, mUserPIN, mLastUsedPIN, mEncryptedPIN, mPIN2; - private static SharedPreferences sharedPreferences=null; - - static void Init(Context context) { - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - mSavedPIN = sharedPreferences.getString("SavedPIN", null); - mUserPIN = null; - mLastUsedPIN = null; - mEncryptedPIN = null; - mPIN2 = null; - } - - - static List getPINs() { - ArrayList result = new ArrayList<>(); - if (mLastUsedPIN != null) result.add(mLastUsedPIN); - if (mEncryptedPIN != null && !result.contains(mEncryptedPIN)) result.add(mEncryptedPIN); - if (mUserPIN != null && !result.contains(mUserPIN)) result.add(mUserPIN); - if (mSavedPIN != null && !result.contains(mSavedPIN)) result.add(mSavedPIN); - if (!result.contains(CardProtocol.DefaultPIN)) result.add(CardProtocol.DefaultPIN); - return result; - } - - static void setLastUsedPIN(String PIN) { - mLastUsedPIN = PIN; - } - - static void setUserPIN(String PIN) { - mUserPIN = PIN; - } - - static void setPIN2(String PIN) { - mPIN2 = PIN; - } - - static void savePIN(String PIN) { - mSavedPIN = PIN; - if (mSavedPIN != null && !mSavedPIN.isEmpty()) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString("SavedPIN", mSavedPIN); - editor.apply(); - } else { - deletePIN(); - } - } - - static void deletePIN() { - SharedPreferences.Editor editor = sharedPreferences.edit(); - if (mSavedPIN != null && mLastUsedPIN != null && mSavedPIN.equals(mLastUsedPIN)) { - mLastUsedPIN = null; - } - mSavedPIN = null; - editor.remove("SavedPIN"); - editor.apply(); - } - - static void saveEncryptedPIN(Cipher cipher, String PIN) { - try { - byte[] iv = cipher.getIV(); - byte[] bytes = cipher.doFinal(PIN.getBytes()); - String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP); - String sIV = Base64.encodeToString(iv, Base64.NO_WRAP); - -// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV)); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString("EncryptedPIN", encryptedPIN); - editor.putString("EncryptedIV", sIV); - editor.apply(); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - static byte[] loadEncryptedIV() { - String sIV = sharedPreferences.getString("EncryptedIV", ""); -// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV)); - - return Base64.decode(sIV, Base64.NO_WRAP); - } - - static String loadEncryptedPIN(Cipher cipher) { - String encryptedPIN = sharedPreferences.getString("EncryptedPIN", null); - - try { - byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP); - mEncryptedPIN = new String(cipher.doFinal(bytes)); -// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN)); - } catch (Exception e) { - e.printStackTrace(); - mEncryptedPIN = null; - } - return mEncryptedPIN; - } - - static boolean haveEncryptedPIN() { - return sharedPreferences.getString("EncryptedPIN", null) != null; - } - - static void deleteEncryptedPIN() { - if (mEncryptedPIN != null && mLastUsedPIN != null && mEncryptedPIN.equals(mLastUsedPIN)) { - mLastUsedPIN = null; - } - mEncryptedPIN = null; - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.remove("EncryptedPIN"); - editor.remove("EncryptedIV"); - editor.apply(); - } - - static void saveEncryptedPIN2(Cipher cipher, String PIN) { - try { - byte[] iv = cipher.getIV(); - byte[] bytes = cipher.doFinal(PIN.getBytes()); - String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP); - String sIV = Base64.encodeToString(iv, Base64.NO_WRAP); - -// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV)); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString("EncryptedPIN2", encryptedPIN); - editor.putString("EncryptedIV2", sIV); - editor.apply(); - - } catch (Exception e) { - e.printStackTrace(); - } - } - - static byte[] loadEncryptedIV2() { - String sIV = sharedPreferences.getString("EncryptedIV2", ""); -// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV)); - - return Base64.decode(sIV, Base64.NO_WRAP); - } - - static String loadEncryptedPIN2(Cipher cipher) { - String encryptedPIN = sharedPreferences.getString("EncryptedPIN2", null); - - try { - byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP); - mPIN2 = new String(cipher.doFinal(bytes)); -// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN)); - } catch (Exception e) { - e.printStackTrace(); - mPIN2 = null; - } - return mPIN2; - } - - static boolean haveEncryptedPIN2() { - return sharedPreferences.getString("EncryptedPIN2", null) != null; - } - - static void deleteEncryptedPIN2() { - mPIN2 = null; - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.remove("EncryptedPIN2"); - editor.remove("EncryptedIV2"); - editor.apply(); - } - - public static String getPIN2() { - return mPIN2; - } - - public static boolean isDefaultPIN(String pin) { - return (pin != null) && (CardProtocol.DefaultPIN.equals(pin)); - } - - public static boolean isDefaultPIN2(String pin2) { - return (pin2 != null) && (CardProtocol.DefaultPIN2.equals(pin2)); - } - - public static String getDefaultPIN() { - return CardProtocol.DefaultPIN; - } - - public static String getDefaultPIN2() { - return CardProtocol.DefaultPIN2; - } - - public static boolean needInit() { - return sharedPreferences==null; - } -} +package com.tangem.wallet; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Base64; +import android.util.Log; + +import com.tangem.cardReader.CardProtocol; + +import java.util.ArrayList; +import java.util.List; + +import javax.crypto.Cipher; + +/** + * Created by dvol on 12.09.2017. + * Global PIN Storage + */ + +class PINStorage { + private static String mSavedPIN, mUserPIN, mLastUsedPIN, mEncryptedPIN, mPIN2; + private static SharedPreferences sharedPreferences=null; + + static void Init(Context context) { + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + mSavedPIN = sharedPreferences.getString("SavedPIN", null); + mUserPIN = null; + mLastUsedPIN = null; + mEncryptedPIN = null; + mPIN2 = null; + } + + + static List getPINs() { + ArrayList result = new ArrayList<>(); + if (mLastUsedPIN != null) result.add(mLastUsedPIN); + if (mEncryptedPIN != null && !result.contains(mEncryptedPIN)) result.add(mEncryptedPIN); + if (mUserPIN != null && !result.contains(mUserPIN)) result.add(mUserPIN); + if (mSavedPIN != null && !result.contains(mSavedPIN)) result.add(mSavedPIN); + if (!result.contains(CardProtocol.DefaultPIN)) result.add(CardProtocol.DefaultPIN); + return result; + } + + static void setLastUsedPIN(String PIN) { + mLastUsedPIN = PIN; + } + + static void setUserPIN(String PIN) { + mUserPIN = PIN; + } + + static void setPIN2(String PIN) { + mPIN2 = PIN; + } + + static void savePIN(String PIN) { + mSavedPIN = PIN; + if (mSavedPIN != null && !mSavedPIN.isEmpty()) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString("SavedPIN", mSavedPIN); + editor.apply(); + } else { + deletePIN(); + } + } + + static void deletePIN() { + SharedPreferences.Editor editor = sharedPreferences.edit(); + if (mSavedPIN != null && mLastUsedPIN != null && mSavedPIN.equals(mLastUsedPIN)) { + mLastUsedPIN = null; + } + mSavedPIN = null; + editor.remove("SavedPIN"); + editor.apply(); + } + + static void saveEncryptedPIN(Cipher cipher, String PIN) { + try { + byte[] iv = cipher.getIV(); + byte[] bytes = cipher.doFinal(PIN.getBytes()); + String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP); + String sIV = Base64.encodeToString(iv, Base64.NO_WRAP); + +// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV)); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString("EncryptedPIN", encryptedPIN); + editor.putString("EncryptedIV", sIV); + editor.apply(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + static byte[] loadEncryptedIV() { + String sIV = sharedPreferences.getString("EncryptedIV", ""); +// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV)); + + return Base64.decode(sIV, Base64.NO_WRAP); + } + + static String loadEncryptedPIN(Cipher cipher) { + String encryptedPIN = sharedPreferences.getString("EncryptedPIN", null); + + try { + byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP); + mEncryptedPIN = new String(cipher.doFinal(bytes)); +// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN)); + } catch (Exception e) { + e.printStackTrace(); + mEncryptedPIN = null; + } + return mEncryptedPIN; + } + + static boolean haveEncryptedPIN() { + return sharedPreferences.getString("EncryptedPIN", null) != null; + } + + static void deleteEncryptedPIN() { + if (mEncryptedPIN != null && mLastUsedPIN != null && mEncryptedPIN.equals(mLastUsedPIN)) { + mLastUsedPIN = null; + } + mEncryptedPIN = null; + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.remove("EncryptedPIN"); + editor.remove("EncryptedIV"); + editor.apply(); + } + + static void saveEncryptedPIN2(Cipher cipher, String PIN) { + try { + byte[] iv = cipher.getIV(); + byte[] bytes = cipher.doFinal(PIN.getBytes()); + String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP); + String sIV = Base64.encodeToString(iv, Base64.NO_WRAP); + +// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV)); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString("EncryptedPIN2", encryptedPIN); + editor.putString("EncryptedIV2", sIV); + editor.apply(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + static byte[] loadEncryptedIV2() { + String sIV = sharedPreferences.getString("EncryptedIV2", ""); +// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV)); + + return Base64.decode(sIV, Base64.NO_WRAP); + } + + static String loadEncryptedPIN2(Cipher cipher) { + String encryptedPIN = sharedPreferences.getString("EncryptedPIN2", null); + + try { + byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP); + mPIN2 = new String(cipher.doFinal(bytes)); +// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN)); + } catch (Exception e) { + e.printStackTrace(); + mPIN2 = null; + } + return mPIN2; + } + + static boolean haveEncryptedPIN2() { + return sharedPreferences.getString("EncryptedPIN2", null) != null; + } + + static void deleteEncryptedPIN2() { + mPIN2 = null; + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.remove("EncryptedPIN2"); + editor.remove("EncryptedIV2"); + editor.apply(); + } + + public static String getPIN2() { + return mPIN2; + } + + public static boolean isDefaultPIN(String pin) { + return (pin != null) && (CardProtocol.DefaultPIN.equals(pin)); + } + + public static boolean isDefaultPIN2(String pin2) { + return (pin2 != null) && (CardProtocol.DefaultPIN2.equals(pin2)); + } + + public static String getDefaultPIN() { + return CardProtocol.DefaultPIN; + } + + public static String getDefaultPIN2() { + return CardProtocol.DefaultPIN2; + } + + public static boolean needInit() { + return sharedPreferences==null; + } +} diff --git a/app/src/main/java/com/tangem/wallet/PhoneUtility.java b/app/src/main/java/com/tangem/wallet/PhoneUtility.java index 617286e30c..7e04e1b7fc 100644 --- a/app/src/main/java/com/tangem/wallet/PhoneUtility.java +++ b/app/src/main/java/com/tangem/wallet/PhoneUtility.java @@ -1,35 +1,35 @@ -package com.tangem.wallet; - -import android.os.Build; - -/** - * Created by Ilia on 20.04.2018. - */ - -public class PhoneUtility { - public static String GetPhoneName() - { - return DeviceName.getDeviceName(); - - } - - public static String getDeviceInfo() { - StringBuilder stringBuilder = new StringBuilder(); - - stringBuilder.append("----------------------------------------\n"); - stringBuilder.append("MODEL: ").append(Build.MODEL).append("\n"); - stringBuilder.append("ID: ").append(Build.ID).append("\n"); - stringBuilder.append("Manufacturer: ").append(Build.MANUFACTURER).append("\n"); - stringBuilder.append("Brand: ").append(Build.BRAND).append("\n"); - stringBuilder.append("Hardware: ").append(Build.HARDWARE).append("\n"); - stringBuilder.append("Version: ").append(Build.VERSION.RELEASE).append(", ").append(Build.VERSION.INCREMENTAL).append("\n"); - stringBuilder.append("OS: ").append(Build.VERSION.BASE_OS).append("\n"); - stringBuilder.append("SDK: ").append(Build.VERSION.SDK_INT).append("\n"); - stringBuilder.append("BOARD: ").append(Build.BOARD).append("\n"); - stringBuilder.append("FINGERPRINT: ").append(Build.FINGERPRINT).append("\n"); - stringBuilder.append("----------------------------------------\n"); - - return stringBuilder.toString(); - - } -} +package com.tangem.wallet; + +import android.os.Build; + +/** + * Created by Ilia on 20.04.2018. + */ + +public class PhoneUtility { + public static String GetPhoneName() + { + return DeviceName.getDeviceName(); + + } + + public static String getDeviceInfo() { + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append("----------------------------------------\n"); + stringBuilder.append("MODEL: ").append(Build.MODEL).append("\n"); + stringBuilder.append("ID: ").append(Build.ID).append("\n"); + stringBuilder.append("Manufacturer: ").append(Build.MANUFACTURER).append("\n"); + stringBuilder.append("Brand: ").append(Build.BRAND).append("\n"); + stringBuilder.append("Hardware: ").append(Build.HARDWARE).append("\n"); + stringBuilder.append("Version: ").append(Build.VERSION.RELEASE).append(", ").append(Build.VERSION.INCREMENTAL).append("\n"); + stringBuilder.append("OS: ").append(Build.VERSION.BASE_OS).append("\n"); + stringBuilder.append("SDK: ").append(Build.VERSION.SDK_INT).append("\n"); + stringBuilder.append("BOARD: ").append(Build.BOARD).append("\n"); + stringBuilder.append("FINGERPRINT: ").append(Build.FINGERPRINT).append("\n"); + stringBuilder.append("----------------------------------------\n"); + + return stringBuilder.toString(); + + } +} diff --git a/app/src/main/java/com/tangem/wallet/PreparePaymentActivity.java b/app/src/main/java/com/tangem/wallet/PreparePaymentActivity.java index 886f711168..acaf5d9b82 100644 --- a/app/src/main/java/com/tangem/wallet/PreparePaymentActivity.java +++ b/app/src/main/java/com/tangem/wallet/PreparePaymentActivity.java @@ -1,237 +1,237 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.text.Editable; -import android.text.Html; -import android.text.Spanned; -import android.text.TextWatcher; -import android.util.Log; -import android.view.View; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.TextView; - -import com.tangem.cardReader.NfcManager; - -import java.io.IOException; - - -public class PreparePaymentActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback { - - private static final int REQUEST_CODE_SCAN_QR = 1; - private static final int REQUEST_CODE_SEND_PAYMENT = 2; - Button btnVerify; - EditText etWallet; - EditText etAmount; - TextView tvCurrency; - TextView tvCardId, tvBalance, tvBalanceEquivalent, tvAmountEquivalent; - ImageView ivCamera; - boolean use_mCurrency; - Tangem_Card mCard; - private NfcManager mNfcManager; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_prepare_payment); - - MainActivity.commonInit(getApplicationContext()); - - mNfcManager = new NfcManager(this, this); - - mCard = new Tangem_Card(getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); - - btnVerify = (Button) findViewById(R.id.btnVerify); - etWallet = (EditText) findViewById(R.id.etWallet); - etAmount = (EditText) findViewById(R.id.etAmount); - ivCamera = (ImageView) findViewById(R.id.ivCamera); - tvCurrency = (TextView) findViewById(R.id.tvCurrency); - tvCardId = (TextView) findViewById(R.id.tvCardID); - tvBalance = (TextView) findViewById(R.id.tvBalance); - tvBalanceEquivalent = (TextView) findViewById(R.id.tvBalanceEquivalent); - tvAmountEquivalent = (TextView) findViewById(R.id.tvAmountEquivalent); - - tvCardId.setText(mCard.getCIDDescription()); - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - - if (mCard.getBlockchain() == Blockchain.Token) { - Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard)); - tvBalance.setText(html); - } else { - tvBalance.setText(engine.GetBalanceWithAlter(mCard)); - } - - tvBalanceEquivalent.setText(engine.GetBalanceEquivalent(mCard)); - - if(etAmount != null && mCard.getRemainingSignatures()<2) - { - etAmount.setEnabled(false); - } - - if( !mCard.getAmountEquivalentDescriptionAvailable()) - { - tvBalanceEquivalent.setError("Service unavailable"); - }else{ - tvBalanceEquivalent.setError(null); - } - - etAmount.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - try { - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - tvAmountEquivalent.setText(engine.GetAmountEqualentDescriptor(mCard, etAmount.getText().toString())); - if (!mCard.getAmountEquivalentDescriptionAvailable()) { - tvAmountEquivalent.setError("Service unavailable"); - }else{ - tvAmountEquivalent.setError(null); - } - } catch (Exception e) { - e.printStackTrace(); - tvAmountEquivalent.setText(""); - } - } - - @Override - public void afterTextChanged(Editable s) { - - } - }); - - if(mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet) - { - tvCurrency.setText(engine.GetBalanceCurrency(mCard)); - use_mCurrency=false; - etAmount.setText(engine.GetBalanceValue(mCard)); - } - else if(mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet) - { - Double balance = engine.GetBalanceLong(mCard) / (mCard.getBlockchain().getMultiplier() / 1000.0); - tvCurrency.setText("m" + mCard.getBlockchain().getCurrency()); - use_mCurrency=true; - String output = FormatUtil.DoubleToString(balance); - etAmount.setText(output); - } - else if(mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet) - { - Double balance = engine.GetBalanceLong(mCard) / (mCard.getBlockchain().getMultiplier() / 1000.0); - tvCurrency.setText("m" + mCard.getBlockchain().getCurrency()); - use_mCurrency=true; - String output = FormatUtil.DoubleToString(balance); - etAmount.setText(output); - } - else - { - tvCurrency.setText(engine.GetBalanceCurrency(mCard)); - use_mCurrency=false; - etAmount.setText(engine.GetBalanceValue(mCard)); - } - - btnVerify.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - String strAmount; - strAmount=etAmount.getText().toString(); - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - try { - if(!engine.CheckAmount(mCard, etAmount.getText().toString())) - { - etAmount.setError("Not enough funds on your card"); - } - } - catch(Exception e) - { - etAmount.setError("Unknown amount format"); - return; - } - - boolean checkAddress = engine.ValdateAddress(etWallet.getText().toString(), mCard); - if(!checkAddress) - { - etWallet.setError("Incorrect destination wallet address"); - return; - } - - if(etWallet.getText().toString().equals(mCard.getWallet())) - { - etWallet.setError("Destination wallet address equal source address"); - return; - } - - Intent intent = new Intent(getBaseContext(), ConfirmPaymentActivity.class); - intent.putExtra("UID", mCard.getUID()); - intent.putExtra("Card", mCard.getAsBundle()); - intent.putExtra("Wallet", etWallet.getText().toString()); - intent.putExtra("Amount", strAmount); - startActivityForResult(intent, REQUEST_CODE_SEND_PAYMENT); - } - }); - - ivCamera.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(getBaseContext(), QRScanActivity.class); - startActivityForResult(intent, REQUEST_CODE_SCAN_QR); - } - }); - - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_CODE_SCAN_QR && resultCode == Activity.RESULT_OK && data != null && data.getExtras().containsKey("QRCode")) { - String code = data.getStringExtra("QRCode"); - if(code.contains("bitcoin:")) - { - String tmp[] = code.split("bitcoin:"); - code = tmp[1]; - } - etWallet.setText(code); - }else if (requestCode == REQUEST_CODE_SEND_PAYMENT ) { - - setResult(resultCode,data); - finish(); - } - } - - @Override - public void onTagDiscovered(Tag tag) { - try { - Log.w(getClass().getName(),"Ignore discovered tag!"); - mNfcManager.IgnoreTag(tag); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Override - public void onResume() { - super.onResume(); - mNfcManager.onResume(); - } - - @Override - public void onPause() { - super.onPause(); - mNfcManager.onPause(); - } - - @Override - public void onStop() { - super.onStop(); - mNfcManager.onStop(); - } +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.text.Editable; +import android.text.Html; +import android.text.Spanned; +import android.text.TextWatcher; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + +import com.tangem.cardReader.NfcManager; + +import java.io.IOException; + + +public class PreparePaymentActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback { + + private static final int REQUEST_CODE_SCAN_QR = 1; + private static final int REQUEST_CODE_SEND_PAYMENT = 2; + Button btnVerify; + EditText etWallet; + EditText etAmount; + TextView tvCurrency; + TextView tvCardId, tvBalance, tvBalanceEquivalent, tvAmountEquivalent; + ImageView ivCamera; + boolean use_mCurrency; + Tangem_Card mCard; + private NfcManager mNfcManager; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_prepare_payment); + + MainActivity.commonInit(getApplicationContext()); + + mNfcManager = new NfcManager(this, this); + + mCard = new Tangem_Card(getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); + + btnVerify = (Button) findViewById(R.id.btnVerify); + etWallet = (EditText) findViewById(R.id.etWallet); + etAmount = (EditText) findViewById(R.id.etAmount); + ivCamera = (ImageView) findViewById(R.id.ivCamera); + tvCurrency = (TextView) findViewById(R.id.tvCurrency); + tvCardId = (TextView) findViewById(R.id.tvCardID); + tvBalance = (TextView) findViewById(R.id.tvBalance); + tvBalanceEquivalent = (TextView) findViewById(R.id.tvBalanceEquivalent); + tvAmountEquivalent = (TextView) findViewById(R.id.tvAmountEquivalent); + + tvCardId.setText(mCard.getCIDDescription()); + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + + if (mCard.getBlockchain() == Blockchain.Token) { + Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard)); + tvBalance.setText(html); + } else { + tvBalance.setText(engine.GetBalanceWithAlter(mCard)); + } + + tvBalanceEquivalent.setText(engine.GetBalanceEquivalent(mCard)); + + if(etAmount != null && mCard.getRemainingSignatures()<2) + { + etAmount.setEnabled(false); + } + + if( !mCard.getAmountEquivalentDescriptionAvailable()) + { + tvBalanceEquivalent.setError("Service unavailable"); + }else{ + tvBalanceEquivalent.setError(null); + } + + etAmount.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + try { + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + tvAmountEquivalent.setText(engine.GetAmountEqualentDescriptor(mCard, etAmount.getText().toString())); + if (!mCard.getAmountEquivalentDescriptionAvailable()) { + tvAmountEquivalent.setError("Service unavailable"); + }else{ + tvAmountEquivalent.setError(null); + } + } catch (Exception e) { + e.printStackTrace(); + tvAmountEquivalent.setText(""); + } + } + + @Override + public void afterTextChanged(Editable s) { + + } + }); + + if(mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet) + { + tvCurrency.setText(engine.GetBalanceCurrency(mCard)); + use_mCurrency=false; + etAmount.setText(engine.GetBalanceValue(mCard)); + } + else if(mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet) + { + Double balance = engine.GetBalanceLong(mCard) / (mCard.getBlockchain().getMultiplier() / 1000.0); + tvCurrency.setText("m" + mCard.getBlockchain().getCurrency()); + use_mCurrency=true; + String output = FormatUtil.DoubleToString(balance); + etAmount.setText(output); + } + else if(mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet) + { + Double balance = engine.GetBalanceLong(mCard) / (mCard.getBlockchain().getMultiplier() / 1000.0); + tvCurrency.setText("m" + mCard.getBlockchain().getCurrency()); + use_mCurrency=true; + String output = FormatUtil.DoubleToString(balance); + etAmount.setText(output); + } + else + { + tvCurrency.setText(engine.GetBalanceCurrency(mCard)); + use_mCurrency=false; + etAmount.setText(engine.GetBalanceValue(mCard)); + } + + btnVerify.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String strAmount; + strAmount=etAmount.getText().toString(); + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + try { + if(!engine.CheckAmount(mCard, etAmount.getText().toString())) + { + etAmount.setError("Not enough funds on your card"); + } + } + catch(Exception e) + { + etAmount.setError("Unknown amount format"); + return; + } + + boolean checkAddress = engine.ValdateAddress(etWallet.getText().toString(), mCard); + if(!checkAddress) + { + etWallet.setError("Incorrect destination wallet address"); + return; + } + + if(etWallet.getText().toString().equals(mCard.getWallet())) + { + etWallet.setError("Destination wallet address equal source address"); + return; + } + + Intent intent = new Intent(getBaseContext(), ConfirmPaymentActivity.class); + intent.putExtra("UID", mCard.getUID()); + intent.putExtra("Card", mCard.getAsBundle()); + intent.putExtra("Wallet", etWallet.getText().toString()); + intent.putExtra("Amount", strAmount); + startActivityForResult(intent, REQUEST_CODE_SEND_PAYMENT); + } + }); + + ivCamera.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(getBaseContext(), QRScanActivity.class); + startActivityForResult(intent, REQUEST_CODE_SCAN_QR); + } + }); + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_CODE_SCAN_QR && resultCode == Activity.RESULT_OK && data != null && data.getExtras().containsKey("QRCode")) { + String code = data.getStringExtra("QRCode"); + if(code.contains("bitcoin:")) + { + String tmp[] = code.split("bitcoin:"); + code = tmp[1]; + } + etWallet.setText(code); + }else if (requestCode == REQUEST_CODE_SEND_PAYMENT ) { + + setResult(resultCode,data); + finish(); + } + } + + @Override + public void onTagDiscovered(Tag tag) { + try { + Log.w(getClass().getName(),"Ignore discovered tag!"); + mNfcManager.IgnoreTag(tag); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void onResume() { + super.onResume(); + mNfcManager.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mNfcManager.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + mNfcManager.onStop(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/PurgeActivity.java b/app/src/main/java/com/tangem/wallet/PurgeActivity.java index 1f53a0a54c..e6d0d47150 100644 --- a/app/src/main/java/com/tangem/wallet/PurgeActivity.java +++ b/app/src/main/java/com/tangem/wallet/PurgeActivity.java @@ -1,333 +1,333 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.IsoDep; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.NfcManager; -import com.tangem.cardReader.Util; - -public class PurgeActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications { - - public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER; - - private Tangem_Card mCard; - private TextView tvCardID; - private NfcManager mNfcManager; - private static final String logTag = "Purge"; - private ProgressBar progressBar; - private PurgeTask purgeTask; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_purge); - - MainActivity.commonInit(getApplicationContext()); - - mCard = new Tangem_Card(getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); - - tvCardID = (TextView) findViewById(R.id.tvCardID); - tvCardID.setText(mCard.getCIDDescription()); - - mNfcManager = new NfcManager(this, this); - - progressBar = (ProgressBar) findViewById(R.id.progressBar); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } - - @Override - public void onTagDiscovered(Tag tag) { - try { - // get IsoDep handle and run cardReader thread - final IsoDep isoDep = IsoDep.get(tag); - if (isoDep == null) { - throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); - } - byte UID[] = tag.getId(); - String sUID = Util.byteArrayToHexString(UID); - Log.v(logTag, "UID: " + sUID); - - if (sUID.equals(mCard.getUID())) { - isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000); - purgeTask = new PurgeTask(isoDep, this); - - purgeTask.start(); - } else { - Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")"); - mNfcManager.IgnoreTag(isoDep.getTag()); - return; - } - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - @Override - public void onResume() { - super.onResume(); - mNfcManager.onResume(); - } - - @Override - public void onPause() { - mNfcManager.onPause(); - if (purgeTask != null) { - purgeTask.cancel(true); - } - super.onPause(); - } - - @Override - public void onStop() { - // dismiss enable NFC dialog - mNfcManager.onStop(); - if (purgeTask != null) { - purgeTask.cancel(true); - } - super.onStop(); - } - -// @Override -// public Dialog CreateNFCDialog(int id, AlertDialogWrapper.Builder builder, LayoutInflater li) { -// return mNfcManager.onCreateDialog(id, builder, li); -// } - - private class PurgeTask extends Thread { - - - private String txOutAddress; - - IsoDep mIsoDep; - CardProtocol.Notifications mNotifications; - private boolean isCancelled = false; - - public PurgeTask(IsoDep isoDep, CardProtocol.Notifications notifications) { - mIsoDep = isoDep; - mNotifications = notifications; - } - - @Override - public void run() { - if (mIsoDep == null) { - return; - } - CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications); - - mNotifications.OnReadStart(protocol); - try { - - // for Samsung's bugs - - // Workaround for the Samsung Galaxy S5 (since the - // first connection always hangs on transceive). - int timeout = mIsoDep.getTimeout(); - mIsoDep.connect(); - mIsoDep.close(); - mIsoDep.connect(); - mIsoDep.setTimeout(timeout); - try { - - mNotifications.OnReadProgress(protocol, 5); - - Log.i("PurgeTask", "[-- Start purge --]"); - - if (isCancelled) return; - - if (mCard.getPauseBeforePIN2() > 0) { - mNotifications.OnReadWait(mCard.getPauseBeforePIN2()); - } - -// try { - protocol.run_PurgeWallet(PINStorage.getPIN2()); -// } finally { -// mNotifications.OnReadWait(0); -// } - - mNotifications.OnReadProgress(protocol, 50); - - protocol.run_Read(); - - mNotifications.OnReadProgress(protocol, 100); - - if (isCancelled) return; - - } finally { - mNfcManager.IgnoreTag(mIsoDep.getTag()); - } - } catch (Exception e) { - e.printStackTrace(); - protocol.setError(e); - - } finally { - Log.i("PurgeTask", "[-- Finish purge --]"); - mNotifications.OnReadFinish(protocol); - } - } - - public void cancel(Boolean AllowInterrupt) { - try { - if (this.isAlive()) { - isCancelled = true; - join(500); - } - if (this.isAlive() && AllowInterrupt) { - interrupt(); - mNotifications.OnReadCancel(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - } - - - public void OnReadStart(CardProtocol cardProtocol) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setVisibility(View.VISIBLE); - progressBar.setProgress(5); - } - }); - } - - public void OnReadFinish(final CardProtocol cardProtocol) { - - purgeTask = null; - - if (cardProtocol != null) { - if (cardProtocol.getError() == null) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); - Intent intent = new Intent(); - intent.putExtra("UID", cardProtocol.getCard().getUID()); - intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); - setResult(Activity.RESULT_OK, intent); - finish(); - } - }); - } else { - if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - } - }); - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - Intent intent = new Intent(); - intent.putExtra("UID", cardProtocol.getCard().getUID()); - intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); - intent.putExtra("message", "Cannot erase wallet. Make sure you enter correct PIN2!"); - setResult(RESULT_INVALID_PIN, intent); - finish(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - return; - } else { - progressBar.post(new Runnable() { - @Override - public void run() { - if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { - if (!NoExtendedLengthSupportDialog.allreadyShowed) { - new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog"); - } - } else { - Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show(); - } - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - } - }); - } - } - } - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - public void OnReadProgress(CardProtocol protocol, final int progress) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(progress); - } - }); - } - - public void OnReadCancel() { - - purgeTask = null; - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - @Override - public void OnReadWait(final int msec) { - WaitSecurityDelayDialog.OnReadWait(this, msec); - } - - @Override - public void OnReadBeforeRequest(int timeout) { - WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout); - } - - @Override - public void OnReadAfterRequest() { - WaitSecurityDelayDialog.onReadAfterRequest(this); - } - -} - +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.NfcManager; +import com.tangem.cardReader.Util; + +public class PurgeActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications { + + public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER; + + private Tangem_Card mCard; + private TextView tvCardID; + private NfcManager mNfcManager; + private static final String logTag = "Purge"; + private ProgressBar progressBar; + private PurgeTask purgeTask; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_purge); + + MainActivity.commonInit(getApplicationContext()); + + mCard = new Tangem_Card(getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); + + tvCardID = (TextView) findViewById(R.id.tvCardID); + tvCardID.setText(mCard.getCIDDescription()); + + mNfcManager = new NfcManager(this, this); + + progressBar = (ProgressBar) findViewById(R.id.progressBar); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } + + @Override + public void onTagDiscovered(Tag tag) { + try { + // get IsoDep handle and run cardReader thread + final IsoDep isoDep = IsoDep.get(tag); + if (isoDep == null) { + throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); + } + byte UID[] = tag.getId(); + String sUID = Util.byteArrayToHexString(UID); + Log.v(logTag, "UID: " + sUID); + + if (sUID.equals(mCard.getUID())) { + isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000); + purgeTask = new PurgeTask(isoDep, this); + + purgeTask.start(); + } else { + Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")"); + mNfcManager.IgnoreTag(isoDep.getTag()); + return; + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public void onResume() { + super.onResume(); + mNfcManager.onResume(); + } + + @Override + public void onPause() { + mNfcManager.onPause(); + if (purgeTask != null) { + purgeTask.cancel(true); + } + super.onPause(); + } + + @Override + public void onStop() { + // dismiss enable NFC dialog + mNfcManager.onStop(); + if (purgeTask != null) { + purgeTask.cancel(true); + } + super.onStop(); + } + +// @Override +// public Dialog CreateNFCDialog(int id, AlertDialogWrapper.Builder builder, LayoutInflater li) { +// return mNfcManager.onCreateDialog(id, builder, li); +// } + + private class PurgeTask extends Thread { + + + private String txOutAddress; + + IsoDep mIsoDep; + CardProtocol.Notifications mNotifications; + private boolean isCancelled = false; + + public PurgeTask(IsoDep isoDep, CardProtocol.Notifications notifications) { + mIsoDep = isoDep; + mNotifications = notifications; + } + + @Override + public void run() { + if (mIsoDep == null) { + return; + } + CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications); + + mNotifications.OnReadStart(protocol); + try { + + // for Samsung's bugs - + // Workaround for the Samsung Galaxy S5 (since the + // first connection always hangs on transceive). + int timeout = mIsoDep.getTimeout(); + mIsoDep.connect(); + mIsoDep.close(); + mIsoDep.connect(); + mIsoDep.setTimeout(timeout); + try { + + mNotifications.OnReadProgress(protocol, 5); + + Log.i("PurgeTask", "[-- Start purge --]"); + + if (isCancelled) return; + + if (mCard.getPauseBeforePIN2() > 0) { + mNotifications.OnReadWait(mCard.getPauseBeforePIN2()); + } + +// try { + protocol.run_PurgeWallet(PINStorage.getPIN2()); +// } finally { +// mNotifications.OnReadWait(0); +// } + + mNotifications.OnReadProgress(protocol, 50); + + protocol.run_Read(); + + mNotifications.OnReadProgress(protocol, 100); + + if (isCancelled) return; + + } finally { + mNfcManager.IgnoreTag(mIsoDep.getTag()); + } + } catch (Exception e) { + e.printStackTrace(); + protocol.setError(e); + + } finally { + Log.i("PurgeTask", "[-- Finish purge --]"); + mNotifications.OnReadFinish(protocol); + } + } + + public void cancel(Boolean AllowInterrupt) { + try { + if (this.isAlive()) { + isCancelled = true; + join(500); + } + if (this.isAlive() && AllowInterrupt) { + interrupt(); + mNotifications.OnReadCancel(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + + public void OnReadStart(CardProtocol cardProtocol) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setVisibility(View.VISIBLE); + progressBar.setProgress(5); + } + }); + } + + public void OnReadFinish(final CardProtocol cardProtocol) { + + purgeTask = null; + + if (cardProtocol != null) { + if (cardProtocol.getError() == null) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); + Intent intent = new Intent(); + intent.putExtra("UID", cardProtocol.getCard().getUID()); + intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); + setResult(Activity.RESULT_OK, intent); + finish(); + } + }); + } else { + if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + } + }); + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + Intent intent = new Intent(); + intent.putExtra("UID", cardProtocol.getCard().getUID()); + intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); + intent.putExtra("message", "Cannot erase wallet. Make sure you enter correct PIN2!"); + setResult(RESULT_INVALID_PIN, intent); + finish(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + return; + } else { + progressBar.post(new Runnable() { + @Override + public void run() { + if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { + if (!NoExtendedLengthSupportDialog.allreadyShowed) { + new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog"); + } + } else { + Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show(); + } + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + } + }); + } + } + } + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + public void OnReadProgress(CardProtocol protocol, final int progress) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(progress); + } + }); + } + + public void OnReadCancel() { + + purgeTask = null; + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + @Override + public void OnReadWait(final int msec) { + WaitSecurityDelayDialog.OnReadWait(this, msec); + } + + @Override + public void OnReadBeforeRequest(int timeout) { + WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout); + } + + @Override + public void OnReadAfterRequest() { + WaitSecurityDelayDialog.onReadAfterRequest(this); + } + +} + diff --git a/app/src/main/java/com/tangem/wallet/QRScanActivity.java b/app/src/main/java/com/tangem/wallet/QRScanActivity.java index bd555763b4..52e658869d 100644 --- a/app/src/main/java/com/tangem/wallet/QRScanActivity.java +++ b/app/src/main/java/com/tangem/wallet/QRScanActivity.java @@ -1,90 +1,90 @@ -package com.tangem.wallet; - -import android.Manifest; -import android.app.Activity; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.support.v4.app.ActivityCompat; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; - -import com.google.zxing.Result; - -import me.dm7.barcodescanner.zxing.ZXingScannerView; - -public class QRScanActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler{ - - private ZXingScannerView mScannerView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - //setContentView(R.layout.activity_qrscan); - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { - Log.e("QRScanActivity","User hasn't granted permission to use camera"); - ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CAMERA}, 1); - }else { - runScanner(); - } - } - - void runScanner() - { - mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view - setContentView(mScannerView); - mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results. - mScannerView.startCamera(); - } - - @Override - public void onRequestPermissionsResult(int requestCode, - String permissions[], int[] grantResults) { - switch (requestCode) { - case 1: { - // If request is cancelled, the result arrays are empty. - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - - Log.i("QRScanActivity","permission was granted"); - // permission was granted, yay! Do the - // contacts-related task you need to do. - runScanner(); - - } else { - Log.e("QRScanActivity","permission denied"); - setResult(Activity.RESULT_CANCELED); - finish(); - - // permission denied, boo! Disable the - // functionality that depends on this permission. - } - return; - } - - // other 'case' lines to check for other - // permissions this app might request - } - } - - - @Override - public void handleResult(Result result) { - Intent data=new Intent(); - data.putExtra("QRCode", result.getText()); - setResult(Activity.RESULT_OK, data); - finish(); - } - - @Override - protected void onPause() { - super.onPause(); - if( mScannerView!=null ) mScannerView.stopCamera(); // Stop camera on pause - } - - @Override - protected void onResume() { - super.onResume(); - if( mScannerView!=null ) mScannerView.startCamera(); - } -} +package com.tangem.wallet; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; + +import com.google.zxing.Result; + +import me.dm7.barcodescanner.zxing.ZXingScannerView; + +public class QRScanActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler{ + + private ZXingScannerView mScannerView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //setContentView(R.layout.activity_qrscan); + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + Log.e("QRScanActivity","User hasn't granted permission to use camera"); + ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CAMERA}, 1); + }else { + runScanner(); + } + } + + void runScanner() + { + mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view + setContentView(mScannerView); + mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results. + mScannerView.startCamera(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], int[] grantResults) { + switch (requestCode) { + case 1: { + // If request is cancelled, the result arrays are empty. + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + + Log.i("QRScanActivity","permission was granted"); + // permission was granted, yay! Do the + // contacts-related task you need to do. + runScanner(); + + } else { + Log.e("QRScanActivity","permission denied"); + setResult(Activity.RESULT_CANCELED); + finish(); + + // permission denied, boo! Disable the + // functionality that depends on this permission. + } + return; + } + + // other 'case' lines to check for other + // permissions this app might request + } + } + + + @Override + public void handleResult(Result result) { + Intent data=new Intent(); + data.putExtra("QRCode", result.getText()); + setResult(Activity.RESULT_OK, data); + finish(); + } + + @Override + protected void onPause() { + super.onPause(); + if( mScannerView!=null ) mScannerView.stopCamera(); // Stop camera on pause + } + + @Override + protected void onResume() { + super.onResume(); + if( mScannerView!=null ) mScannerView.startCamera(); + } +} diff --git a/app/src/main/java/com/tangem/wallet/RLP.java b/app/src/main/java/com/tangem/wallet/RLP.java index b3d85d7687..17ae006e1a 100644 --- a/app/src/main/java/com/tangem/wallet/RLP.java +++ b/app/src/main/java/com/tangem/wallet/RLP.java @@ -1,228 +1,228 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 07.01.2018. - */ - -import java.util.Arrays; - -import static com.tangem.wallet.ByteUtil.isNullOrZeroArray; -import static com.tangem.wallet.ByteUtil.isSingleZero; - -public class RLP { - public static final byte[] EMPTY_ELEMENT_RLP = encodeElement(new byte[0]); - - /** - * Allow for content up to size of 2^64 bytes * - */ - private static final double MAX_ITEM_LENGTH = Math.pow(256, 8); - - /** - * Reason for threshold according to Vitalik Buterin: - * - 56 bytes maximizes the benefit of both options - * - if we went with 60 then we would have only had 4 slots for long strings - * so RLP would not have been able to store objects above 4gb - * - if we went with 48 then RLP would be fine for 2^128 space, but that's way too much - * - so 56 and 2^64 space seems like the right place to put the cutoff - * - also, that's where Bitcoin's varint does the cutof - */ - private static final int SIZE_THRESHOLD = 56; - - /** RLP encoding rules are defined as follows: */ - - /* - * For a single byte whose value is in the [0x00, 0x7f] range, that byte is - * its own RLP encoding. - */ - - /** - * [0x80] - * If a string is 0-55 bytes long, the RLP encoding consists of a single - * byte with value 0x80 plus the length of the string followed by the - * string. The range of the first byte is thus [0x80, 0xb7]. - */ - private static final int OFFSET_SHORT_ITEM = 0x80; - - /** - * [0xb7] - * If a string is more than 55 bytes long, the RLP encoding consists of a - * single byte with value 0xb7 plus the length of the length of the string - * in binary form, followed by the length of the string, followed by the - * string. For example, a length-1024 string would be encoded as - * \xb9\x04\x00 followed by the string. The range of the first byte is thus - * [0xb8, 0xbf]. - */ - private static final int OFFSET_LONG_ITEM = 0xb7; - - /** - * [0xc0] - * If the total payload of a list (i.e. the combined length of all its - * items) is 0-55 bytes long, the RLP encoding consists of a single byte - * with value 0xc0 plus the length of the list followed by the concatenation - * of the RLP encodings of the items. The range of the first byte is thus - * [0xc0, 0xf7]. - */ - private static final int OFFSET_SHORT_LIST = 0xc0; - - public static byte[] encodeByte(byte singleByte) { - if ((singleByte & 0xFF) == 0) { - return new byte[]{(byte) OFFSET_SHORT_ITEM}; - } else if ((singleByte & 0xFF) <= 0x7F) { - return new byte[]{singleByte}; - } else { - return new byte[]{(byte) (OFFSET_SHORT_ITEM + 1), singleByte}; - } - } - - public static byte[] encodeShort(short singleShort) { - if ((singleShort & 0xFF) == singleShort) - return encodeByte((byte) singleShort); - else { - return new byte[]{(byte) (OFFSET_SHORT_ITEM + 2), - (byte) (singleShort >> 8 & 0xFF), - (byte) (singleShort >> 0 & 0xFF)}; - } - } - - public static byte[] encodeInt(int singleInt) { - if ((singleInt & 0xFF) == singleInt) - return encodeByte((byte) singleInt); - else if ((singleInt & 0xFFFF) == singleInt) - return encodeShort((short) singleInt); - else if ((singleInt & 0xFFFFFF) == singleInt) - return new byte[]{(byte) (OFFSET_SHORT_ITEM + 3), - (byte) (singleInt >>> 16), - (byte) (singleInt >>> 8), - (byte) singleInt}; - else { - return new byte[]{(byte) (OFFSET_SHORT_ITEM + 4), - (byte) (singleInt >>> 24), - (byte) (singleInt >>> 16), - (byte) (singleInt >>> 8), - (byte) singleInt}; - } - } - - private static final int OFFSET_LONG_LIST = 0xf7; - - public static byte[] encodeElement2(byte[] srcData) { - if (srcData == null) - return new byte[]{(byte) OFFSET_SHORT_ITEM}; - else if (srcData.length == 1 && (srcData[0] & 0xFF) < 0x80) { - return srcData; - } else if (srcData.length < SIZE_THRESHOLD) { - // length = 8X - byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length); - byte[] data = Arrays.copyOf(srcData, srcData.length + 1); - System.arraycopy(data, 0, data, 1, srcData.length); - data[0] = length; - - return data; - } else { - // length of length = BX - // prefix = [BX, [length]] - int tmpLength = srcData.length; - byte byteNum = 0; - while (tmpLength != 0) { - ++byteNum; - tmpLength = tmpLength >> 8; - } - byte[] lenBytes = new byte[byteNum]; - for (int i = 0; i < byteNum; ++i) { - lenBytes[byteNum - 1 - i] = (byte) ((srcData.length >> (8 * i)) & 0xFF); - } - // first byte = F7 + bytes.length - byte[] data = Arrays.copyOf(srcData, srcData.length + 1 + byteNum); - System.arraycopy(data, 0, data, 1 + byteNum, srcData.length); - data[0] = (byte) (OFFSET_LONG_ITEM + byteNum); - System.arraycopy(lenBytes, 0, data, 1, lenBytes.length); - - return data; - } - } - public static byte[] encodeElement(byte[] srcData) { - - if (isNullOrZeroArray(srcData)) - return new byte[]{(byte) OFFSET_SHORT_ITEM}; - else if (isSingleZero(srcData)) - return srcData; - else if (srcData.length == 1 && (srcData[0] & 0xFF) < 0x80) { - return srcData; - } else if (srcData.length < SIZE_THRESHOLD) { - // length = 8X - byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length); - byte[] data = Arrays.copyOf(srcData, srcData.length + 1); - System.arraycopy(data, 0, data, 1, srcData.length); - data[0] = length; - - return data; - } else { - // length of length = BX - // prefix = [BX, [length]] - int tmpLength = srcData.length; - byte byteNum = 0; - while (tmpLength != 0) { - ++byteNum; - tmpLength = tmpLength >> 8; - } - byte[] lenBytes = new byte[byteNum]; - for (int i = 0; i < byteNum; ++i) { - lenBytes[byteNum - 1 - i] = (byte) ((srcData.length >> (8 * i)) & 0xFF); - } - // first byte = F7 + bytes.length - byte[] data = Arrays.copyOf(srcData, srcData.length + 1 + byteNum); - System.arraycopy(data, 0, data, 1 + byteNum, srcData.length); - data[0] = (byte) (OFFSET_LONG_ITEM + byteNum); - System.arraycopy(lenBytes, 0, data, 1, lenBytes.length); - - return data; - } - } - - - public static byte[] encodeList(byte[]... elements) { - - if (elements == null) { - return new byte[]{(byte) OFFSET_SHORT_LIST}; - } - - int totalLength = 0; - for (byte[] element1 : elements) { - totalLength += element1.length; - } - - byte[] data; - int copyPos; - if (totalLength < SIZE_THRESHOLD) { - - data = new byte[1 + totalLength]; - data[0] = (byte) (OFFSET_SHORT_LIST + totalLength); - copyPos = 1; - } else { - // length of length = BX - // prefix = [BX, [length]] - int tmpLength = totalLength; - byte byteNum = 0; - while (tmpLength != 0) { - ++byteNum; - tmpLength = tmpLength >> 8; - } - tmpLength = totalLength; - byte[] lenBytes = new byte[byteNum]; - for (int i = 0; i < byteNum; ++i) { - lenBytes[byteNum - 1 - i] = (byte) ((tmpLength >> (8 * i)) & 0xFF); - } - // first byte = F7 + bytes.length - data = new byte[1 + lenBytes.length + totalLength]; - data[0] = (byte) (OFFSET_LONG_LIST + byteNum); - System.arraycopy(lenBytes, 0, data, 1, lenBytes.length); - - copyPos = lenBytes.length + 1; - } - for (byte[] element : elements) { - System.arraycopy(element, 0, data, copyPos, element.length); - copyPos += element.length; - } - return data; - } +package com.tangem.wallet; + +/** + * Created by Ilia on 07.01.2018. + */ + +import java.util.Arrays; + +import static com.tangem.wallet.ByteUtil.isNullOrZeroArray; +import static com.tangem.wallet.ByteUtil.isSingleZero; + +public class RLP { + public static final byte[] EMPTY_ELEMENT_RLP = encodeElement(new byte[0]); + + /** + * Allow for content up to size of 2^64 bytes * + */ + private static final double MAX_ITEM_LENGTH = Math.pow(256, 8); + + /** + * Reason for threshold according to Vitalik Buterin: + * - 56 bytes maximizes the benefit of both options + * - if we went with 60 then we would have only had 4 slots for long strings + * so RLP would not have been able to store objects above 4gb + * - if we went with 48 then RLP would be fine for 2^128 space, but that's way too much + * - so 56 and 2^64 space seems like the right place to put the cutoff + * - also, that's where Bitcoin's varint does the cutof + */ + private static final int SIZE_THRESHOLD = 56; + + /** RLP encoding rules are defined as follows: */ + + /* + * For a single byte whose value is in the [0x00, 0x7f] range, that byte is + * its own RLP encoding. + */ + + /** + * [0x80] + * If a string is 0-55 bytes long, the RLP encoding consists of a single + * byte with value 0x80 plus the length of the string followed by the + * string. The range of the first byte is thus [0x80, 0xb7]. + */ + private static final int OFFSET_SHORT_ITEM = 0x80; + + /** + * [0xb7] + * If a string is more than 55 bytes long, the RLP encoding consists of a + * single byte with value 0xb7 plus the length of the length of the string + * in binary form, followed by the length of the string, followed by the + * string. For example, a length-1024 string would be encoded as + * \xb9\x04\x00 followed by the string. The range of the first byte is thus + * [0xb8, 0xbf]. + */ + private static final int OFFSET_LONG_ITEM = 0xb7; + + /** + * [0xc0] + * If the total payload of a list (i.e. the combined length of all its + * items) is 0-55 bytes long, the RLP encoding consists of a single byte + * with value 0xc0 plus the length of the list followed by the concatenation + * of the RLP encodings of the items. The range of the first byte is thus + * [0xc0, 0xf7]. + */ + private static final int OFFSET_SHORT_LIST = 0xc0; + + public static byte[] encodeByte(byte singleByte) { + if ((singleByte & 0xFF) == 0) { + return new byte[]{(byte) OFFSET_SHORT_ITEM}; + } else if ((singleByte & 0xFF) <= 0x7F) { + return new byte[]{singleByte}; + } else { + return new byte[]{(byte) (OFFSET_SHORT_ITEM + 1), singleByte}; + } + } + + public static byte[] encodeShort(short singleShort) { + if ((singleShort & 0xFF) == singleShort) + return encodeByte((byte) singleShort); + else { + return new byte[]{(byte) (OFFSET_SHORT_ITEM + 2), + (byte) (singleShort >> 8 & 0xFF), + (byte) (singleShort >> 0 & 0xFF)}; + } + } + + public static byte[] encodeInt(int singleInt) { + if ((singleInt & 0xFF) == singleInt) + return encodeByte((byte) singleInt); + else if ((singleInt & 0xFFFF) == singleInt) + return encodeShort((short) singleInt); + else if ((singleInt & 0xFFFFFF) == singleInt) + return new byte[]{(byte) (OFFSET_SHORT_ITEM + 3), + (byte) (singleInt >>> 16), + (byte) (singleInt >>> 8), + (byte) singleInt}; + else { + return new byte[]{(byte) (OFFSET_SHORT_ITEM + 4), + (byte) (singleInt >>> 24), + (byte) (singleInt >>> 16), + (byte) (singleInt >>> 8), + (byte) singleInt}; + } + } + + private static final int OFFSET_LONG_LIST = 0xf7; + + public static byte[] encodeElement2(byte[] srcData) { + if (srcData == null) + return new byte[]{(byte) OFFSET_SHORT_ITEM}; + else if (srcData.length == 1 && (srcData[0] & 0xFF) < 0x80) { + return srcData; + } else if (srcData.length < SIZE_THRESHOLD) { + // length = 8X + byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length); + byte[] data = Arrays.copyOf(srcData, srcData.length + 1); + System.arraycopy(data, 0, data, 1, srcData.length); + data[0] = length; + + return data; + } else { + // length of length = BX + // prefix = [BX, [length]] + int tmpLength = srcData.length; + byte byteNum = 0; + while (tmpLength != 0) { + ++byteNum; + tmpLength = tmpLength >> 8; + } + byte[] lenBytes = new byte[byteNum]; + for (int i = 0; i < byteNum; ++i) { + lenBytes[byteNum - 1 - i] = (byte) ((srcData.length >> (8 * i)) & 0xFF); + } + // first byte = F7 + bytes.length + byte[] data = Arrays.copyOf(srcData, srcData.length + 1 + byteNum); + System.arraycopy(data, 0, data, 1 + byteNum, srcData.length); + data[0] = (byte) (OFFSET_LONG_ITEM + byteNum); + System.arraycopy(lenBytes, 0, data, 1, lenBytes.length); + + return data; + } + } + public static byte[] encodeElement(byte[] srcData) { + + if (isNullOrZeroArray(srcData)) + return new byte[]{(byte) OFFSET_SHORT_ITEM}; + else if (isSingleZero(srcData)) + return srcData; + else if (srcData.length == 1 && (srcData[0] & 0xFF) < 0x80) { + return srcData; + } else if (srcData.length < SIZE_THRESHOLD) { + // length = 8X + byte length = (byte) (OFFSET_SHORT_ITEM + srcData.length); + byte[] data = Arrays.copyOf(srcData, srcData.length + 1); + System.arraycopy(data, 0, data, 1, srcData.length); + data[0] = length; + + return data; + } else { + // length of length = BX + // prefix = [BX, [length]] + int tmpLength = srcData.length; + byte byteNum = 0; + while (tmpLength != 0) { + ++byteNum; + tmpLength = tmpLength >> 8; + } + byte[] lenBytes = new byte[byteNum]; + for (int i = 0; i < byteNum; ++i) { + lenBytes[byteNum - 1 - i] = (byte) ((srcData.length >> (8 * i)) & 0xFF); + } + // first byte = F7 + bytes.length + byte[] data = Arrays.copyOf(srcData, srcData.length + 1 + byteNum); + System.arraycopy(data, 0, data, 1 + byteNum, srcData.length); + data[0] = (byte) (OFFSET_LONG_ITEM + byteNum); + System.arraycopy(lenBytes, 0, data, 1, lenBytes.length); + + return data; + } + } + + + public static byte[] encodeList(byte[]... elements) { + + if (elements == null) { + return new byte[]{(byte) OFFSET_SHORT_LIST}; + } + + int totalLength = 0; + for (byte[] element1 : elements) { + totalLength += element1.length; + } + + byte[] data; + int copyPos; + if (totalLength < SIZE_THRESHOLD) { + + data = new byte[1 + totalLength]; + data[0] = (byte) (OFFSET_SHORT_LIST + totalLength); + copyPos = 1; + } else { + // length of length = BX + // prefix = [BX, [length]] + int tmpLength = totalLength; + byte byteNum = 0; + while (tmpLength != 0) { + ++byteNum; + tmpLength = tmpLength >> 8; + } + tmpLength = totalLength; + byte[] lenBytes = new byte[byteNum]; + for (int i = 0; i < byteNum; ++i) { + lenBytes[byteNum - 1 - i] = (byte) ((tmpLength >> (8 * i)) & 0xFF); + } + // first byte = F7 + bytes.length + data = new byte[1 + lenBytes.length + totalLength]; + data[0] = (byte) (OFFSET_LONG_LIST + byteNum); + System.arraycopy(lenBytes, 0, data, 1, lenBytes.length); + + copyPos = lenBytes.length + 1; + } + for (byte[] element : elements) { + System.arraycopy(element, 0, data, copyPos, element.length); + copyPos += element.length; + } + return data; + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/RLPElement.java b/app/src/main/java/com/tangem/wallet/RLPElement.java index ae82526c2d..3706e0be22 100644 --- a/app/src/main/java/com/tangem/wallet/RLPElement.java +++ b/app/src/main/java/com/tangem/wallet/RLPElement.java @@ -1,12 +1,12 @@ -package com.tangem.wallet; - -import java.io.Serializable; - -/** - * Created by Ilia on 07.01.2018. - */ - -public interface RLPElement extends Serializable { - - byte[] getRLPData(); -} +package com.tangem.wallet; + +import java.io.Serializable; + +/** + * Created by Ilia on 07.01.2018. + */ + +public interface RLPElement extends Serializable { + + byte[] getRLPData(); +} diff --git a/app/src/main/java/com/tangem/wallet/RLPList.java b/app/src/main/java/com/tangem/wallet/RLPList.java index 14164502b3..94dad1521f 100644 --- a/app/src/main/java/com/tangem/wallet/RLPList.java +++ b/app/src/main/java/com/tangem/wallet/RLPList.java @@ -1,37 +1,37 @@ -package com.tangem.wallet; - -import java.util.ArrayList; - -/** - * Created by Ilia on 07.01.2018. - */ - -public class RLPList extends ArrayList implements RLPElement { - - byte[] rlpData; - - public void setRLPData(byte[] rlpData) { - this.rlpData = rlpData; - } - - public byte[] getRLPData() { - return rlpData; - } - - public static void recursivePrint(RLPElement element) { - - if (element == null) - throw new RuntimeException("RLPElement object can't be null"); - if (element instanceof RLPList) { - - RLPList rlpList = (RLPList) element; - System.out.print("["); - for (RLPElement singleElement : rlpList) - recursivePrint(singleElement); - System.out.print("]"); - } else { - String hex = BTCUtils.toHex(element.getRLPData()); - System.out.print(hex + ", "); - } - } -} +package com.tangem.wallet; + +import java.util.ArrayList; + +/** + * Created by Ilia on 07.01.2018. + */ + +public class RLPList extends ArrayList implements RLPElement { + + byte[] rlpData; + + public void setRLPData(byte[] rlpData) { + this.rlpData = rlpData; + } + + public byte[] getRLPData() { + return rlpData; + } + + public static void recursivePrint(RLPElement element) { + + if (element == null) + throw new RuntimeException("RLPElement object can't be null"); + if (element instanceof RLPList) { + + RLPList rlpList = (RLPList) element; + System.out.print("["); + for (RLPElement singleElement : rlpList) + recursivePrint(singleElement); + System.out.print("]"); + } else { + String hex = BTCUtils.toHex(element.getRLPData()); + System.out.print(hex + ", "); + } + } +} diff --git a/app/src/main/java/com/tangem/wallet/SelectBlockchainActivity.java b/app/src/main/java/com/tangem/wallet/SelectBlockchainActivity.java index 635daf6791..baf77bbb62 100644 --- a/app/src/main/java/com/tangem/wallet/SelectBlockchainActivity.java +++ b/app/src/main/java/com/tangem/wallet/SelectBlockchainActivity.java @@ -1,31 +1,31 @@ -package com.tangem.wallet; - -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; - -public class SelectBlockchainActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_select_blockchain); - -// Spinner spBlockchain = (Spinner) findViewById(R.id.spBlockchain); -// ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, Blockchain.values()); -// spBlockchain.setAdapter(adapter); -// spBlockchain.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { -// @Override -// public void onItemSelected(AdapterView parent, View view, int position, long id) { -// Intent intent = new Intent(); -// intent.putExtra("blockchain", Blockchain.values()[position].toString()); -// setResult(RESULT_OK, intent); -// finish(); -// } -// -// @Override -// public void onNothingSelected(AdapterView parent) { -// -// } -// }); - } -} +package com.tangem.wallet; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +public class SelectBlockchainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_select_blockchain); + +// Spinner spBlockchain = (Spinner) findViewById(R.id.spBlockchain); +// ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, Blockchain.values()); +// spBlockchain.setAdapter(adapter); +// spBlockchain.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { +// @Override +// public void onItemSelected(AdapterView parent, View view, int position, long id) { +// Intent intent = new Intent(); +// intent.putExtra("blockchain", Blockchain.values()[position].toString()); +// setResult(RESULT_OK, intent); +// finish(); +// } +// +// @Override +// public void onNothingSelected(AdapterView parent) { +// +// } +// }); + } +} diff --git a/app/src/main/java/com/tangem/wallet/SendTransactionActivity.java b/app/src/main/java/com/tangem/wallet/SendTransactionActivity.java index 84c64e2f88..33c8d46d65 100644 --- a/app/src/main/java/com/tangem/wallet/SendTransactionActivity.java +++ b/app/src/main/java/com/tangem/wallet/SendTransactionActivity.java @@ -1,208 +1,208 @@ -package com.tangem.wallet; - -import android.content.Intent; -import android.os.AsyncTask; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.util.Log; -import android.view.KeyEvent; -import android.widget.ProgressBar; -import android.widget.Toast; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.math.BigInteger; -import java.util.List; - -public class SendTransactionActivity extends AppCompatActivity { - - ProgressBar progressBar; - private Tangem_Card mCard; - private String tx; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_send_transaction); - - MainActivity.commonInit(getApplicationContext()); - - progressBar = findViewById(R.id.progressBar); - - Intent intent = getIntent(); - mCard = new Tangem_Card(getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(intent.getExtras().getBundle("Card")); - tx = intent.getStringExtra("TX"); - - CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); - if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet || mCard.getBlockchain() == Blockchain.Token) { - ETHRequestTask task = new ETHRequestTask(mCard.getBlockchain()); - Infura_Request req = Infura_Request.SendTransaction(mCard.getWallet(), tx); - req.setID(67); - req.setBlockchain(mCard.getBlockchain()); - task.execute(req); - } else if (mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet ) { - String nodeAddress = engine.GetNode(mCard); - int nodePort = engine.GetNodePort(mCard); - ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort); - connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx)); - } - else if (mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet ) { - String nodeAddress = engine.GetNode(mCard); - int nodePort = engine.GetNodePort(mCard); - ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort); - connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx)); - } - - } - - @Override - public boolean onKeyDown(int keycode, KeyEvent e) { - switch (keycode) { - case KeyEvent.KEYCODE_BACK: - Toast.makeText(getBaseContext(),"Please wait while the payment is sent...",Toast.LENGTH_LONG).show(); - return true; - } - - return super.onKeyDown(keycode, e); - } - - void FinishWithError(String Message) { - Intent intent = new Intent(); - intent.putExtra("message", "Failed to send transaction. Try again."); - setResult(MainActivity.RESULT_CANCELED, intent); - finish(); - } - - void FinishWithSuccess() { - Intent intent = new Intent(); - intent.putExtra("message", "Transaction has been successfully signed and sent to blockchain node. Wallet balance will be updated in a while"); - setResult(MainActivity.RESULT_OK, intent); - finish(); - } - - private class ETHRequestTask extends Infura_Task { - ETHRequestTask(Blockchain blockchain){ - super(blockchain); - } - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - for (Infura_Request request : requests) { - try { - if (request.error == null) { - if (request.isMethod(Infura_Request.METHOD_ETH_SendRawTransaction)) { - try { - String hashTX = ""; - try { - String tmp = request.getResultString(); - hashTX = tmp; - }catch(JSONException e) - { - JSONObject msg = request.getAnswer(); - JSONObject err = msg.getJSONObject("error"); - hashTX = err.getString("message"); - LastSignStorage.setLastMessage(mCard.getWallet(), hashTX); - FinishWithError(hashTX); - return; - } - - try { - if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { - hashTX = hashTX.substring(2); - } - BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ - LastSignStorage.setTxWasSend(mCard.getWallet()); - LastSignStorage.setLastMessage(mCard.getWallet(), ""); - BigInteger nonce = mCard.GetConfirmTXCount(); - nonce.add(BigInteger.valueOf(1)); - mCard.SetConfirmTXCount(nonce); - Log.e("TX_RESULT", hashTX); - FinishWithSuccess(); - }catch(Exception e) - { - FinishWithError(hashTX); - } - - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(e.toString()); - } - } - } else if (request.error != null) { - FinishWithError(request.error); - } - } catch (JSONException e) { - e.printStackTrace(); - FinishWithError(e.toString()); - } - } - } - } - - private class ConnectTask extends Electrum_Task { - public ConnectTask(String host, int port) { - super(host, port); - } - - public ConnectTask(String host, int port, SharedData sharedData) { - super(host, port, sharedData); - } - - @Override - protected void onProgressUpdate(Integer... values) { - super.onProgressUpdate(values); - } - - @Override - protected void onPostExecute(List requests) { - super.onPostExecute(requests); - CoinEngine engine = CoinEngineFactory.Create(Blockchain.Bitcoin); - - for (Electrum_Request request : requests) { - try { - if (request.error == null) { - if (request.isMethod(Electrum_Request.METHOD_SendTransaction)) { - try { - String hashTX = request.getResultString(); - - try - { - LastSignStorage.setLastMessage(mCard.getWallet(), hashTX); - if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { - hashTX = hashTX.substring(2); - } - BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ - LastSignStorage.setTxWasSend(mCard.getWallet()); - LastSignStorage.setLastMessage(mCard.getWallet(), ""); - Log.e("TX_RESULT", hashTX); - FinishWithSuccess(); - }catch(Exception e) - { - engine.SwitchNode(null); - FinishWithError(hashTX); - return; - } - - } catch (JSONException e) { - e.printStackTrace(); - engine.SwitchNode(null); - FinishWithError(e.toString()); - } - } - } else if (request.error != null) { - engine.SwitchNode(null); - FinishWithError(request.error); - } - } catch (JSONException e) { - e.printStackTrace(); - engine.SwitchNode(null); - FinishWithError(e.toString()); - } - } - - } - } - -} +package com.tangem.wallet; + +import android.content.Intent; +import android.os.AsyncTask; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.widget.ProgressBar; +import android.widget.Toast; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.math.BigInteger; +import java.util.List; + +public class SendTransactionActivity extends AppCompatActivity { + + ProgressBar progressBar; + private Tangem_Card mCard; + private String tx; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_send_transaction); + + MainActivity.commonInit(getApplicationContext()); + + progressBar = findViewById(R.id.progressBar); + + Intent intent = getIntent(); + mCard = new Tangem_Card(getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(intent.getExtras().getBundle("Card")); + tx = intent.getStringExtra("TX"); + + CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain()); + if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet || mCard.getBlockchain() == Blockchain.Token) { + ETHRequestTask task = new ETHRequestTask(mCard.getBlockchain()); + Infura_Request req = Infura_Request.SendTransaction(mCard.getWallet(), tx); + req.setID(67); + req.setBlockchain(mCard.getBlockchain()); + task.execute(req); + } else if (mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet ) { + String nodeAddress = engine.GetNode(mCard); + int nodePort = engine.GetNodePort(mCard); + ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort); + connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx)); + } + else if (mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet ) { + String nodeAddress = engine.GetNode(mCard); + int nodePort = engine.GetNodePort(mCard); + ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort); + connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx)); + } + + } + + @Override + public boolean onKeyDown(int keycode, KeyEvent e) { + switch (keycode) { + case KeyEvent.KEYCODE_BACK: + Toast.makeText(getBaseContext(),"Please wait while the payment is sent...",Toast.LENGTH_LONG).show(); + return true; + } + + return super.onKeyDown(keycode, e); + } + + void FinishWithError(String Message) { + Intent intent = new Intent(); + intent.putExtra("message", "Failed to send transaction. Try again."); + setResult(MainActivity.RESULT_CANCELED, intent); + finish(); + } + + void FinishWithSuccess() { + Intent intent = new Intent(); + intent.putExtra("message", "Transaction has been successfully signed and sent to blockchain node. Wallet balance will be updated in a while"); + setResult(MainActivity.RESULT_OK, intent); + finish(); + } + + private class ETHRequestTask extends Infura_Task { + ETHRequestTask(Blockchain blockchain){ + super(blockchain); + } + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + for (Infura_Request request : requests) { + try { + if (request.error == null) { + if (request.isMethod(Infura_Request.METHOD_ETH_SendRawTransaction)) { + try { + String hashTX = ""; + try { + String tmp = request.getResultString(); + hashTX = tmp; + }catch(JSONException e) + { + JSONObject msg = request.getAnswer(); + JSONObject err = msg.getJSONObject("error"); + hashTX = err.getString("message"); + LastSignStorage.setLastMessage(mCard.getWallet(), hashTX); + FinishWithError(hashTX); + return; + } + + try { + if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { + hashTX = hashTX.substring(2); + } + BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ + LastSignStorage.setTxWasSend(mCard.getWallet()); + LastSignStorage.setLastMessage(mCard.getWallet(), ""); + BigInteger nonce = mCard.GetConfirmTXCount(); + nonce.add(BigInteger.valueOf(1)); + mCard.SetConfirmTXCount(nonce); + Log.e("TX_RESULT", hashTX); + FinishWithSuccess(); + }catch(Exception e) + { + FinishWithError(hashTX); + } + + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(e.toString()); + } + } + } else if (request.error != null) { + FinishWithError(request.error); + } + } catch (JSONException e) { + e.printStackTrace(); + FinishWithError(e.toString()); + } + } + } + } + + private class ConnectTask extends Electrum_Task { + public ConnectTask(String host, int port) { + super(host, port); + } + + public ConnectTask(String host, int port, SharedData sharedData) { + super(host, port, sharedData); + } + + @Override + protected void onProgressUpdate(Integer... values) { + super.onProgressUpdate(values); + } + + @Override + protected void onPostExecute(List requests) { + super.onPostExecute(requests); + CoinEngine engine = CoinEngineFactory.Create(Blockchain.Bitcoin); + + for (Electrum_Request request : requests) { + try { + if (request.error == null) { + if (request.isMethod(Electrum_Request.METHOD_SendTransaction)) { + try { + String hashTX = request.getResultString(); + + try + { + LastSignStorage.setLastMessage(mCard.getWallet(), hashTX); + if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) { + hashTX = hashTX.substring(2); + } + BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ + LastSignStorage.setTxWasSend(mCard.getWallet()); + LastSignStorage.setLastMessage(mCard.getWallet(), ""); + Log.e("TX_RESULT", hashTX); + FinishWithSuccess(); + }catch(Exception e) + { + engine.SwitchNode(null); + FinishWithError(hashTX); + return; + } + + } catch (JSONException e) { + e.printStackTrace(); + engine.SwitchNode(null); + FinishWithError(e.toString()); + } + } + } else if (request.error != null) { + engine.SwitchNode(null); + FinishWithError(request.error); + } + } catch (JSONException e) { + e.printStackTrace(); + engine.SwitchNode(null); + FinishWithError(e.toString()); + } + } + + } + } + +} diff --git a/app/src/main/java/com/tangem/wallet/SharedData.java b/app/src/main/java/com/tangem/wallet/SharedData.java index 45fcfa5def..92e9fbd8ef 100644 --- a/app/src/main/java/com/tangem/wallet/SharedData.java +++ b/app/src/main/java/com/tangem/wallet/SharedData.java @@ -1,22 +1,22 @@ -package com.tangem.wallet; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Created by Ilia on 12.04.2018. - */ - - -public class SharedData -{ - public static int COUNT_REQUEST = 3; - public AtomicInteger requestCounter; - public int allRequest; - public AtomicInteger errorRequest; - public SharedData(int requstCount) - { - allRequest = requstCount; - errorRequest = new AtomicInteger(0); - requestCounter = new AtomicInteger(0); - } +package com.tangem.wallet; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by Ilia on 12.04.2018. + */ + + +public class SharedData +{ + public static int COUNT_REQUEST = 3; + public AtomicInteger requestCounter; + public int allRequest; + public AtomicInteger errorRequest; + public SharedData(int requstCount) + { + allRequest = requstCount; + errorRequest = new AtomicInteger(0); + requestCounter = new AtomicInteger(0); + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/SwapPINActivity.java b/app/src/main/java/com/tangem/wallet/SwapPINActivity.java index 94b2342ee5..67910d1505 100644 --- a/app/src/main/java/com/tangem/wallet/SwapPINActivity.java +++ b/app/src/main/java/com/tangem/wallet/SwapPINActivity.java @@ -1,326 +1,326 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.content.res.ColorStateList; -import android.graphics.Color; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.IsoDep; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; - -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.NfcManager; -import com.tangem.cardReader.Util; - -public class SwapPINActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications { - - public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER; - private Tangem_Card mCard; - private NfcManager mNfcManager; - private static final String logTag = "SwapPIN"; - private ProgressBar progressBar; - private SwapPINTask swapPinTask; - - private String newPIN, newPIN2; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_swap_pin); - - MainActivity.commonInit(getApplicationContext()); - - mCard = new Tangem_Card(getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); - - newPIN = getIntent().getStringExtra("newPIN"); - newPIN2 = getIntent().getStringExtra("newPIN2"); - - TextView tvCardID = findViewById(R.id.tvCardID); - tvCardID.setText(mCard.getCIDDescription()); - - mNfcManager = new NfcManager(this, this); - - progressBar = findViewById(R.id.progressBar); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } - - @Override - public void onTagDiscovered(Tag tag) { - try { - // get IsoDep handle and run cardReader thread - final IsoDep isoDep = IsoDep.get(tag); - if (isoDep == null) { - throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); - } - byte UID[] = tag.getId(); - String sUID = Util.byteArrayToHexString(UID); - Log.v(logTag, "UID: " + sUID); - - if (sUID.equals(mCard.getUID())) { - isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000); - swapPinTask = new SwapPINTask(isoDep, this); - swapPinTask.start(); - } else { - Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")"); - mNfcManager.IgnoreTag(isoDep.getTag()); - } - - } catch (Exception e) { - e.printStackTrace(); - } - - } - - @Override - public void onResume() { - super.onResume(); - mNfcManager.onResume(); - } - - @Override - public void onPause() { - mNfcManager.onPause(); - if (swapPinTask != null) { - swapPinTask.cancel(true); - } - super.onPause(); - } - - @Override - public void onStop() { - // dismiss enable NFC dialog - mNfcManager.onStop(); - if (swapPinTask != null) { - swapPinTask.cancel(true); - } - super.onStop(); - } - - private class SwapPINTask extends Thread { - - IsoDep mIsoDep; - CardProtocol.Notifications mNotifications; - private boolean isCancelled = false; - - SwapPINTask(IsoDep isoDep, CardProtocol.Notifications notifications) { - mIsoDep = isoDep; - mNotifications = notifications; - } - - @Override - public void run() { - if (mIsoDep == null) { - return; - } - CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications); - - mNotifications.OnReadStart(protocol); - try { - - // for Samsung's bugs - - // Workaround for the Samsung Galaxy S5 (since the - // first connection always hangs on transceive). - int timeout = mIsoDep.getTimeout(); - mIsoDep.connect(); - mIsoDep.close(); - mIsoDep.connect(); - mIsoDep.setTimeout(timeout); - try { - - mNotifications.OnReadProgress(protocol, 5); - - Log.i("SwapTask", "[-- Start swap pin --]"); - - if (isCancelled) return; - - if (mCard.getPauseBeforePIN2() > 0) { - mNotifications.OnReadWait(mCard.getPauseBeforePIN2()); - } - -// try { - protocol.run_SwapPIN(PINStorage.getPIN2(), newPIN, newPIN2, false); - protocol.setPIN(newPIN); - mCard.setPIN(newPIN); -// } finally { -// mNotifications.OnReadWait(0); -// } - - mNotifications.OnReadProgress(protocol, 50); - - protocol.run_Read(); - - mNotifications.OnReadProgress(protocol, 100); - - } finally { - mNfcManager.IgnoreTag(mIsoDep.getTag()); - } - } catch (Exception e) { - e.printStackTrace(); - protocol.setError(e); - - } finally { - Log.i("SwapPINTask", "[-- Finish purge --]"); - mNotifications.OnReadFinish(protocol); - } - } - - public void cancel(Boolean AllowInterrupt) { - try { - if (this.isAlive()) { - isCancelled = true; - join(500); - } - if (this.isAlive() && AllowInterrupt) { - interrupt(); - mNotifications.OnReadCancel(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - } - - - public void OnReadStart(CardProtocol cardProtocol) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setVisibility(View.VISIBLE); - progressBar.setProgress(5); - } - }); - } - - public void OnReadFinish(final CardProtocol cardProtocol) { - - swapPinTask = null; - - if (cardProtocol != null) { - if (cardProtocol.getError() == null) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); - Intent intent = new Intent(); - intent.putExtra("UID", cardProtocol.getCard().getUID()); - intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); - setResult(Activity.RESULT_OK, intent); - finish(); - } - }); - } else if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - } - }); - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - Intent intent = new Intent(); - intent.putExtra("message", "Cannot change PIN(s). Make sure you enter correct PIN2!"); - intent.putExtra("UID", cardProtocol.getCard().getUID()); - intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); - setResult(RESULT_INVALID_PIN, intent); - finish(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - return; - } else { - - progressBar.post(new Runnable() { - @Override - public void run() { - if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { - if (!NoExtendedLengthSupportDialog.allreadyShowed) { - new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog"); - } - } else { - Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show(); - } - progressBar.setProgress(100); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); - } - }); - - } - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - } - - public void OnReadProgress(CardProtocol protocol, final int progress) { - progressBar.post(new Runnable() { - @Override - public void run() { - progressBar.setProgress(progress); - } - }); - } - - public void OnReadCancel() { - - swapPinTask = null; - - progressBar.postDelayed(new Runnable() { - @Override - public void run() { - try { - progressBar.setProgress(0); - progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); - progressBar.setVisibility(View.INVISIBLE); - } catch (Exception e) { - e.printStackTrace(); - } - } - }, 500); - } - - @Override - public void OnReadWait(final int msec) { - WaitSecurityDelayDialog.OnReadWait(this, msec); - } - - @Override - public void OnReadBeforeRequest(int timeout) { - WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout); - } - - @Override - public void OnReadAfterRequest() { - WaitSecurityDelayDialog.onReadAfterRequest(this); - } - -} - +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.NfcManager; +import com.tangem.cardReader.Util; + +public class SwapPINActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications { + + public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER; + private Tangem_Card mCard; + private NfcManager mNfcManager; + private static final String logTag = "SwapPIN"; + private ProgressBar progressBar; + private SwapPINTask swapPinTask; + + private String newPIN, newPIN2; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_swap_pin); + + MainActivity.commonInit(getApplicationContext()); + + mCard = new Tangem_Card(getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card")); + + newPIN = getIntent().getStringExtra("newPIN"); + newPIN2 = getIntent().getStringExtra("newPIN2"); + + TextView tvCardID = findViewById(R.id.tvCardID); + tvCardID.setText(mCard.getCIDDescription()); + + mNfcManager = new NfcManager(this, this); + + progressBar = findViewById(R.id.progressBar); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } + + @Override + public void onTagDiscovered(Tag tag) { + try { + // get IsoDep handle and run cardReader thread + final IsoDep isoDep = IsoDep.get(tag); + if (isoDep == null) { + throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err)); + } + byte UID[] = tag.getId(); + String sUID = Util.byteArrayToHexString(UID); + Log.v(logTag, "UID: " + sUID); + + if (sUID.equals(mCard.getUID())) { + isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000); + swapPinTask = new SwapPINTask(isoDep, this); + swapPinTask.start(); + } else { + Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")"); + mNfcManager.IgnoreTag(isoDep.getTag()); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Override + public void onResume() { + super.onResume(); + mNfcManager.onResume(); + } + + @Override + public void onPause() { + mNfcManager.onPause(); + if (swapPinTask != null) { + swapPinTask.cancel(true); + } + super.onPause(); + } + + @Override + public void onStop() { + // dismiss enable NFC dialog + mNfcManager.onStop(); + if (swapPinTask != null) { + swapPinTask.cancel(true); + } + super.onStop(); + } + + private class SwapPINTask extends Thread { + + IsoDep mIsoDep; + CardProtocol.Notifications mNotifications; + private boolean isCancelled = false; + + SwapPINTask(IsoDep isoDep, CardProtocol.Notifications notifications) { + mIsoDep = isoDep; + mNotifications = notifications; + } + + @Override + public void run() { + if (mIsoDep == null) { + return; + } + CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications); + + mNotifications.OnReadStart(protocol); + try { + + // for Samsung's bugs - + // Workaround for the Samsung Galaxy S5 (since the + // first connection always hangs on transceive). + int timeout = mIsoDep.getTimeout(); + mIsoDep.connect(); + mIsoDep.close(); + mIsoDep.connect(); + mIsoDep.setTimeout(timeout); + try { + + mNotifications.OnReadProgress(protocol, 5); + + Log.i("SwapTask", "[-- Start swap pin --]"); + + if (isCancelled) return; + + if (mCard.getPauseBeforePIN2() > 0) { + mNotifications.OnReadWait(mCard.getPauseBeforePIN2()); + } + +// try { + protocol.run_SwapPIN(PINStorage.getPIN2(), newPIN, newPIN2, false); + protocol.setPIN(newPIN); + mCard.setPIN(newPIN); +// } finally { +// mNotifications.OnReadWait(0); +// } + + mNotifications.OnReadProgress(protocol, 50); + + protocol.run_Read(); + + mNotifications.OnReadProgress(protocol, 100); + + } finally { + mNfcManager.IgnoreTag(mIsoDep.getTag()); + } + } catch (Exception e) { + e.printStackTrace(); + protocol.setError(e); + + } finally { + Log.i("SwapPINTask", "[-- Finish purge --]"); + mNotifications.OnReadFinish(protocol); + } + } + + public void cancel(Boolean AllowInterrupt) { + try { + if (this.isAlive()) { + isCancelled = true; + join(500); + } + if (this.isAlive() && AllowInterrupt) { + interrupt(); + mNotifications.OnReadCancel(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + } + + + public void OnReadStart(CardProtocol cardProtocol) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setVisibility(View.VISIBLE); + progressBar.setProgress(5); + } + }); + } + + public void OnReadFinish(final CardProtocol cardProtocol) { + + swapPinTask = null; + + if (cardProtocol != null) { + if (cardProtocol.getError() == null) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN)); + Intent intent = new Intent(); + intent.putExtra("UID", cardProtocol.getCard().getUID()); + intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); + setResult(Activity.RESULT_OK, intent); + finish(); + } + }); + } else if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + } + }); + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + Intent intent = new Intent(); + intent.putExtra("message", "Cannot change PIN(s). Make sure you enter correct PIN2!"); + intent.putExtra("UID", cardProtocol.getCard().getUID()); + intent.putExtra("Card", cardProtocol.getCard().getAsBundle()); + setResult(RESULT_INVALID_PIN, intent); + finish(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + return; + } else { + + progressBar.post(new Runnable() { + @Override + public void run() { + if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) { + if (!NoExtendedLengthSupportDialog.allreadyShowed) { + new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog"); + } + } else { + Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show(); + } + progressBar.setProgress(100); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED)); + } + }); + + } + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + } + + public void OnReadProgress(CardProtocol protocol, final int progress) { + progressBar.post(new Runnable() { + @Override + public void run() { + progressBar.setProgress(progress); + } + }); + } + + public void OnReadCancel() { + + swapPinTask = null; + + progressBar.postDelayed(new Runnable() { + @Override + public void run() { + try { + progressBar.setProgress(0); + progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY)); + progressBar.setVisibility(View.INVISIBLE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }, 500); + } + + @Override + public void OnReadWait(final int msec) { + WaitSecurityDelayDialog.OnReadWait(this, msec); + } + + @Override + public void OnReadBeforeRequest(int timeout) { + WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout); + } + + @Override + public void OnReadAfterRequest() { + WaitSecurityDelayDialog.onReadAfterRequest(this); + } + +} + diff --git a/app/src/main/java/com/tangem/wallet/TokenEngine.java b/app/src/main/java/com/tangem/wallet/TokenEngine.java index d9975bd2db..6faad29a6a 100644 --- a/app/src/main/java/com/tangem/wallet/TokenEngine.java +++ b/app/src/main/java/com/tangem/wallet/TokenEngine.java @@ -1,436 +1,436 @@ -package com.tangem.wallet; - -import android.net.Uri; -import android.util.Log; - -import com.google.common.base.Strings; -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.TLV; - -import org.bitcoinj.core.ECKey; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.math.RoundingMode; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.Date; - -import static com.tangem.wallet.FormatUtil.GetDecimalFormat; - -/** - * Created by Ilia on 20.03.2018. - */ - -public class TokenEngine extends CoinEngine{ - public String GetNextNode(Tangem_Card mCard) - { - return "abc1.hsmiths.com"; - } - public int GetNextNodePort(Tangem_Card mCard) - { - return 60001; - } - public String GetNode(Tangem_Card mCard) - { - return "abc1.hsmiths.com"; - } - public int GetNodePort(Tangem_Card mCard) - { - return 60001; - } - public void SwitchNode(Tangem_Card mCard) - { - } - public boolean AwaitingConfirmation(Tangem_Card card) - { - return false; - } - - public boolean InOutPutVisible() - { - return false; - } - - public String GetBalanceCurrency(Tangem_Card card) - { - String currency = card.getTokenSymbol(); - if(Strings.isNullOrEmpty(currency)) - return "NoN"; - return currency; - } - - public String GetFeeCurrency() - { - return "Gwei"; - } - - BigDecimal convertToEth(String value) - { - BigInteger m = new BigInteger(value, 10); - BigDecimal n = new BigDecimal(m); - BigDecimal d = n.divide(new BigDecimal("1000000000000000000")); - d = d.setScale(8, RoundingMode.DOWN); - return d; - } - - - public int GetTokenDecimals(Tangem_Card card) - { - return card.getTokensDecimal(); - } - - public String GetContractAddress(Tangem_Card card) - { - return card.getContractAddress(); - } - public boolean IsNeedCheckNode() - { - return false; - } - - public boolean ValdateAddress(String address, Tangem_Card card) { - if (address == null || address.isEmpty()) { - return false; - } - - if(!address.startsWith("0x")&&!address.startsWith("0X")) - { - return false; - } - - if(address.length()!=42) - { - return false; - } - - return true; - } - - public String GetBalanceAlterValue(Tangem_Card mCard) - { - String dec = mCard.getDecimalBalanceAlter(); - BigDecimal d = convertToEth(dec); - String s = d.toString(); - - String pattern = "#0.000"; // If you like 4 zeros - DecimalFormat myFormatter = new DecimalFormat(pattern); - String output = myFormatter.format(d); - return output; - } - - public String GetBalanceValue(Tangem_Card mCard) - { - if(!HasBalanceInfo(mCard)) - return "-- -- -- " + GetBalanceCurrency(mCard); - - String dec = mCard.getDecimalBalance(); - BigDecimal d = new BigDecimal(dec); - BigDecimal p = new BigDecimal(10); - p = p.pow(GetTokenDecimals(mCard)); - BigDecimal l = d.divide(p); - - String pattern = "#0.000"; // If you like 4 zeros - DecimalFormat myFormatter = new DecimalFormat(pattern); - String output = myFormatter.format(l); - return output; - } - - public boolean CheckAmount(Tangem_Card card, String amount) throws Exception - { - DecimalFormat decimalFormat = GetDecimalFormat(); - BigDecimal amountValue = (BigDecimal) decimalFormat.parse(amount); //new BigDecimal(strAmount); - BigDecimal maxValue = new BigDecimal(GetBalanceValue(card)); - if(amountValue.compareTo(maxValue) > 0 ) - { - return false; - } - - return true; - } - - public Long GetBalanceLong(Tangem_Card mCard) - { - return mCard.getBalance(); - } - - public boolean IsBalanceAlterNotZero(Tangem_Card card) - { - String balance = card.getDecimalBalanceAlter(); - if(balance == null || balance == "") - return false; - - BigDecimal bi = new BigDecimal(balance); - - if (BigDecimal.ZERO.compareTo(bi) == 0) - return false; - - return true; - } - - public boolean IsBalanceNotZero(Tangem_Card card) - { - String balance = card.getDecimalBalance(); - if(balance == null || balance == "") - return false; - - BigDecimal bi = new BigDecimal(balance); - - if (BigDecimal.ZERO.compareTo(bi) == 0) - return false; - - return true; - } - - public boolean HasBalanceInfo(Tangem_Card card) - { - String balance = card.getDecimalBalance(); - if(balance == null || balance == "") - return false; - - String balanceEx = card.getDecimalBalanceAlter(); - if(balanceEx == null || balanceEx == "") - return false; - return true; - } - - @Override - public String GetBalanceEquivalent(Tangem_Card mCard) { - if(!HasBalanceInfo(mCard)){ - return "-- -- -- "; - } - String dec = mCard.getDecimalBalance(); - BigDecimal d = convertToEth(dec); - return EthEngine.getAmountEquivalentDescriptionETH(d, mCard.getRate()); - } - - @Override - public String GetBalance(Tangem_Card mCard) { - if(!HasBalanceInfo(mCard)){ - return "-- -- -- " + GetBalanceCurrency(mCard); - } - - String output = GetBalanceValue(mCard); - String s = output + " " + GetBalanceCurrency(mCard); - return s; - } - - - - - public String GetBalanceWithAlter(Tangem_Card mCard) - { - //return GetBalance(mCard) + "\n(" + GetBalanceAlterValue(mCard) + " ETH)"; - return " " + GetBalance(mCard) + "
+ " + GetBalanceAlterValue(mCard) + " ETH for gas"; - } - - public String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException { - Keccak256 kec = new Keccak256(); - int lenPk = pkUncompressed.length; - if (lenPk < 2) { - throw new IllegalArgumentException("Uncompress public key length is invald"); - } - byte[] cleanKey = new byte[lenPk - 1]; - for (int i = 0; i < cleanKey.length; ++i) { - cleanKey[i] = pkUncompressed[i + 1]; - } - byte[] r = kec.digest(cleanKey); - - byte[] address = new byte[20]; - for (int i = 0; i < 20; ++i) { - address[i] = r[i + 12]; - } - - return String.format("0x%s", BTCUtils.toHex(address)); - } - - @Override - public String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception { - throw new Exception("Not implemented"); - } - - @Override - public byte[] ConvertAmountToByteArray(Tangem_Card mCard, String amount) throws Exception { - throw new Exception("Not implemented"); - } - - @Override - public String GetAmountDescription(Tangem_Card mCard, String amount) throws Exception { - throw new Exception("Not implemented"); - } - - - public String GetAmountEqualentDescriptor(Tangem_Card mCard, String value) - { - BigDecimal d = new BigDecimal(value); - return EthEngine.getAmountEquivalentDescriptionETH(d, mCard.getRate()); - } - - public String GetFeeEqualentDescriptor(Tangem_Card mCard, String value) - { - BigDecimal d = new BigDecimal(value); - return EthEngine.getAmountEquivalentDescriptionETH(d, mCard.getRateAlter()); - } - - public Uri getShareWalletURIExplorer(Tangem_Card mCard) - { - return Uri.parse("https://etherscan.io/token/"+GetContractAddress(mCard)+"?a=" + mCard.getWallet()); - } - - public Uri getShareWalletURI(Tangem_Card mCard) - { - return Uri.parse("" + mCard.getWallet()); - } - - public boolean CheckUnspentTransaction(Tangem_Card mCard) - { - return true; - } - - public boolean CheckAmountValie(Tangem_Card mCard, String amountValue, String feeValue, Long minFeeInInternalUnits) - { - Long fee = null; - BigDecimal amount = null; - try { - amount = new BigDecimal(GetBalanceAlterValue(mCard));//mCard.InternalUnitsFromString(amountValue); - fee = mCard.InternalUnitsFromString(feeValue); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - - if(fee == null || amount == null) - return false; - - if(fee == 0 || amount.compareTo(BigDecimal.ZERO) == 0) - return false; - - - if(fee < minFeeInInternalUnits) - return false; - - - BigDecimal tmpFee = new BigDecimal(feeValue); - BigDecimal tmpAmount = amount; - tmpAmount = tmpAmount.multiply(new BigDecimal("1000000000")); - - if (tmpFee.compareTo(tmpAmount) > 0) - return false; - - return true; - } - - public String EvaluteFeeEquivalent(Tangem_Card mCard, String fee) - { - BigDecimal gweFee = new BigDecimal(fee); - gweFee = gweFee.divide(new BigDecimal("1000000000")); - gweFee = gweFee.setScale(18, RoundingMode.DOWN); - return GetFeeEqualentDescriptor(mCard, gweFee.toString()); - } - - public byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception { - - BigInteger nonceValue = mCard.GetConfirmTXCount(); - byte[] pbKey = mCard.getWalletPublicKey(); - boolean flag = (mCard.getSigningMethod()== Tangem_Card.SigningMethod.Sign_Hash_Validated_By_Issuer); - Issuer issuer = mCard.getIssuer(); - - - BigInteger fee = new BigInteger(feeValue, 10); - - BigDecimal amountDecValue = new BigDecimal(amountValue); - - int d = GetTokenDecimals(mCard); - BigDecimal amountDec = new BigDecimal("10"); - amountDec = amountDec.pow(d); - amountDec = amountDecValue.multiply(amountDec); - - //amountDec = amountDec.multiply(new BigDecimal("1000000000")); - - - BigInteger amount = amountDec.toBigInteger(); //new BigInteger(amountValue, 10); - - - - - //amount = amount.subtract(fee); - - BigInteger nonce = nonceValue; - BigInteger gasPrice = fee.divide(BigInteger.valueOf(21000)); - BigInteger gasLimit = BigInteger.valueOf(60000); - Integer chainId = ETH_Transaction.ChainEnum.Mainnet.getValue(); - BigInteger amountZero = BigInteger.ZERO; - - Long multiplicator = 1000000000L; - - gasPrice = gasPrice.multiply(BigInteger.valueOf(multiplicator)); - - String to = toValue; - - if (to.startsWith("0x") || to.startsWith("0X")) { - to = to.substring(2); - } - - String contractAddress = GetContractAddress(mCard); - - if (contractAddress.startsWith("0x") || contractAddress.startsWith("0X")) { - contractAddress = contractAddress.substring(2); - } - - String amountLeadZero = amount.toString(16); - if (amountLeadZero.startsWith("0x") || amountLeadZero.startsWith("0X")) { - amountLeadZero = amountLeadZero.substring(2); - } - - while(amountLeadZero.length() < 64) - { - amountLeadZero = "0" + amountLeadZero; - } - - String cmd = "a9059cbb000000000000000000000000"+to+amountLeadZero; //TODO only for BAT - - - byte[] data = BTCUtils.fromHex(cmd); - ETH_Transaction tx = ETH_Transaction.create(contractAddress, amountZero, nonce, gasPrice, gasLimit, chainId, data); - - byte[][] hashesForSign = new byte[1][]; - byte[] for_hash = tx.getRawHash(); - hashesForSign[0] = for_hash; - - byte[] signFromCard = null; - try { - signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, flag, null, issuer).getTLV(TLV.Tag.TAG_Signature).Value; - // TODO slice signFromCard to hashes.length parts - } catch (Exception ex) { - Log.e("ETH", ex.getMessage()); - return null; - } - - LastSignStorage.setLastSignDate(mCard.getWallet(), new Date()); - - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); - s = CryptoUtil.toCanonicalised(s); - - boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey); - - if(!f) - { - Log.e("ETH-CHECK", "Sign Failed."); - } - - tx.signature = new ECDSASignature_ETH(r, s); - int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey); - if (v != 27 && v != 28) { - Log.e("ETH", "invalid v"); - return null; - } - tx.signature.v = (byte) v; - Log.e("ETH_v", String.valueOf(v)); - - byte[] realTX = tx.getEncoded(); - return realTX; - } -} +package com.tangem.wallet; + +import android.net.Uri; +import android.util.Log; + +import com.google.common.base.Strings; +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.TLV; + +import org.bitcoinj.core.ECKey; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.Date; + +import static com.tangem.wallet.FormatUtil.GetDecimalFormat; + +/** + * Created by Ilia on 20.03.2018. + */ + +public class TokenEngine extends CoinEngine{ + public String GetNextNode(Tangem_Card mCard) + { + return "abc1.hsmiths.com"; + } + public int GetNextNodePort(Tangem_Card mCard) + { + return 60001; + } + public String GetNode(Tangem_Card mCard) + { + return "abc1.hsmiths.com"; + } + public int GetNodePort(Tangem_Card mCard) + { + return 60001; + } + public void SwitchNode(Tangem_Card mCard) + { + } + public boolean AwaitingConfirmation(Tangem_Card card) + { + return false; + } + + public boolean InOutPutVisible() + { + return false; + } + + public String GetBalanceCurrency(Tangem_Card card) + { + String currency = card.getTokenSymbol(); + if(Strings.isNullOrEmpty(currency)) + return "NoN"; + return currency; + } + + public String GetFeeCurrency() + { + return "Gwei"; + } + + BigDecimal convertToEth(String value) + { + BigInteger m = new BigInteger(value, 10); + BigDecimal n = new BigDecimal(m); + BigDecimal d = n.divide(new BigDecimal("1000000000000000000")); + d = d.setScale(8, RoundingMode.DOWN); + return d; + } + + + public int GetTokenDecimals(Tangem_Card card) + { + return card.getTokensDecimal(); + } + + public String GetContractAddress(Tangem_Card card) + { + return card.getContractAddress(); + } + public boolean IsNeedCheckNode() + { + return false; + } + + public boolean ValdateAddress(String address, Tangem_Card card) { + if (address == null || address.isEmpty()) { + return false; + } + + if(!address.startsWith("0x")&&!address.startsWith("0X")) + { + return false; + } + + if(address.length()!=42) + { + return false; + } + + return true; + } + + public String GetBalanceAlterValue(Tangem_Card mCard) + { + String dec = mCard.getDecimalBalanceAlter(); + BigDecimal d = convertToEth(dec); + String s = d.toString(); + + String pattern = "#0.000"; // If you like 4 zeros + DecimalFormat myFormatter = new DecimalFormat(pattern); + String output = myFormatter.format(d); + return output; + } + + public String GetBalanceValue(Tangem_Card mCard) + { + if(!HasBalanceInfo(mCard)) + return "-- -- -- " + GetBalanceCurrency(mCard); + + String dec = mCard.getDecimalBalance(); + BigDecimal d = new BigDecimal(dec); + BigDecimal p = new BigDecimal(10); + p = p.pow(GetTokenDecimals(mCard)); + BigDecimal l = d.divide(p); + + String pattern = "#0.000"; // If you like 4 zeros + DecimalFormat myFormatter = new DecimalFormat(pattern); + String output = myFormatter.format(l); + return output; + } + + public boolean CheckAmount(Tangem_Card card, String amount) throws Exception + { + DecimalFormat decimalFormat = GetDecimalFormat(); + BigDecimal amountValue = (BigDecimal) decimalFormat.parse(amount); //new BigDecimal(strAmount); + BigDecimal maxValue = new BigDecimal(GetBalanceValue(card)); + if(amountValue.compareTo(maxValue) > 0 ) + { + return false; + } + + return true; + } + + public Long GetBalanceLong(Tangem_Card mCard) + { + return mCard.getBalance(); + } + + public boolean IsBalanceAlterNotZero(Tangem_Card card) + { + String balance = card.getDecimalBalanceAlter(); + if(balance == null || balance == "") + return false; + + BigDecimal bi = new BigDecimal(balance); + + if (BigDecimal.ZERO.compareTo(bi) == 0) + return false; + + return true; + } + + public boolean IsBalanceNotZero(Tangem_Card card) + { + String balance = card.getDecimalBalance(); + if(balance == null || balance == "") + return false; + + BigDecimal bi = new BigDecimal(balance); + + if (BigDecimal.ZERO.compareTo(bi) == 0) + return false; + + return true; + } + + public boolean HasBalanceInfo(Tangem_Card card) + { + String balance = card.getDecimalBalance(); + if(balance == null || balance == "") + return false; + + String balanceEx = card.getDecimalBalanceAlter(); + if(balanceEx == null || balanceEx == "") + return false; + return true; + } + + @Override + public String GetBalanceEquivalent(Tangem_Card mCard) { + if(!HasBalanceInfo(mCard)){ + return "-- -- -- "; + } + String dec = mCard.getDecimalBalance(); + BigDecimal d = convertToEth(dec); + return EthEngine.getAmountEquivalentDescriptionETH(d, mCard.getRate()); + } + + @Override + public String GetBalance(Tangem_Card mCard) { + if(!HasBalanceInfo(mCard)){ + return "-- -- -- " + GetBalanceCurrency(mCard); + } + + String output = GetBalanceValue(mCard); + String s = output + " " + GetBalanceCurrency(mCard); + return s; + } + + + + + public String GetBalanceWithAlter(Tangem_Card mCard) + { + //return GetBalance(mCard) + "\n(" + GetBalanceAlterValue(mCard) + " ETH)"; + return " " + GetBalance(mCard) + "
+ " + GetBalanceAlterValue(mCard) + " ETH for gas"; + } + + public String calculateAddress(Tangem_Card mCard, byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException { + Keccak256 kec = new Keccak256(); + int lenPk = pkUncompressed.length; + if (lenPk < 2) { + throw new IllegalArgumentException("Uncompress public key length is invald"); + } + byte[] cleanKey = new byte[lenPk - 1]; + for (int i = 0; i < cleanKey.length; ++i) { + cleanKey[i] = pkUncompressed[i + 1]; + } + byte[] r = kec.digest(cleanKey); + + byte[] address = new byte[20]; + for (int i = 0; i < 20; ++i) { + address[i] = r[i + 12]; + } + + return String.format("0x%s", BTCUtils.toHex(address)); + } + + @Override + public String ConvertByteArrayToAmount(Tangem_Card mCard, byte[] bytes) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public byte[] ConvertAmountToByteArray(Tangem_Card mCard, String amount) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public String GetAmountDescription(Tangem_Card mCard, String amount) throws Exception { + throw new Exception("Not implemented"); + } + + + public String GetAmountEqualentDescriptor(Tangem_Card mCard, String value) + { + BigDecimal d = new BigDecimal(value); + return EthEngine.getAmountEquivalentDescriptionETH(d, mCard.getRate()); + } + + public String GetFeeEqualentDescriptor(Tangem_Card mCard, String value) + { + BigDecimal d = new BigDecimal(value); + return EthEngine.getAmountEquivalentDescriptionETH(d, mCard.getRateAlter()); + } + + public Uri getShareWalletURIExplorer(Tangem_Card mCard) + { + return Uri.parse("https://etherscan.io/token/"+GetContractAddress(mCard)+"?a=" + mCard.getWallet()); + } + + public Uri getShareWalletURI(Tangem_Card mCard) + { + return Uri.parse("" + mCard.getWallet()); + } + + public boolean CheckUnspentTransaction(Tangem_Card mCard) + { + return true; + } + + public boolean CheckAmountValie(Tangem_Card mCard, String amountValue, String feeValue, Long minFeeInInternalUnits) + { + Long fee = null; + BigDecimal amount = null; + try { + amount = new BigDecimal(GetBalanceAlterValue(mCard));//mCard.InternalUnitsFromString(amountValue); + fee = mCard.InternalUnitsFromString(feeValue); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + if(fee == null || amount == null) + return false; + + if(fee == 0 || amount.compareTo(BigDecimal.ZERO) == 0) + return false; + + + if(fee < minFeeInInternalUnits) + return false; + + + BigDecimal tmpFee = new BigDecimal(feeValue); + BigDecimal tmpAmount = amount; + tmpAmount = tmpAmount.multiply(new BigDecimal("1000000000")); + + if (tmpFee.compareTo(tmpAmount) > 0) + return false; + + return true; + } + + public String EvaluteFeeEquivalent(Tangem_Card mCard, String fee) + { + BigDecimal gweFee = new BigDecimal(fee); + gweFee = gweFee.divide(new BigDecimal("1000000000")); + gweFee = gweFee.setScale(18, RoundingMode.DOWN); + return GetFeeEqualentDescriptor(mCard, gweFee.toString()); + } + + public byte[] Sign(String feeValue, String amountValue, String toValue, Tangem_Card mCard, CardProtocol protocol) throws Exception { + + BigInteger nonceValue = mCard.GetConfirmTXCount(); + byte[] pbKey = mCard.getWalletPublicKey(); + boolean flag = (mCard.getSigningMethod()== Tangem_Card.SigningMethod.Sign_Hash_Validated_By_Issuer); + Issuer issuer = mCard.getIssuer(); + + + BigInteger fee = new BigInteger(feeValue, 10); + + BigDecimal amountDecValue = new BigDecimal(amountValue); + + int d = GetTokenDecimals(mCard); + BigDecimal amountDec = new BigDecimal("10"); + amountDec = amountDec.pow(d); + amountDec = amountDecValue.multiply(amountDec); + + //amountDec = amountDec.multiply(new BigDecimal("1000000000")); + + + BigInteger amount = amountDec.toBigInteger(); //new BigInteger(amountValue, 10); + + + + + //amount = amount.subtract(fee); + + BigInteger nonce = nonceValue; + BigInteger gasPrice = fee.divide(BigInteger.valueOf(21000)); + BigInteger gasLimit = BigInteger.valueOf(60000); + Integer chainId = ETH_Transaction.ChainEnum.Mainnet.getValue(); + BigInteger amountZero = BigInteger.ZERO; + + Long multiplicator = 1000000000L; + + gasPrice = gasPrice.multiply(BigInteger.valueOf(multiplicator)); + + String to = toValue; + + if (to.startsWith("0x") || to.startsWith("0X")) { + to = to.substring(2); + } + + String contractAddress = GetContractAddress(mCard); + + if (contractAddress.startsWith("0x") || contractAddress.startsWith("0X")) { + contractAddress = contractAddress.substring(2); + } + + String amountLeadZero = amount.toString(16); + if (amountLeadZero.startsWith("0x") || amountLeadZero.startsWith("0X")) { + amountLeadZero = amountLeadZero.substring(2); + } + + while(amountLeadZero.length() < 64) + { + amountLeadZero = "0" + amountLeadZero; + } + + String cmd = "a9059cbb000000000000000000000000"+to+amountLeadZero; //TODO only for BAT + + + byte[] data = BTCUtils.fromHex(cmd); + ETH_Transaction tx = ETH_Transaction.create(contractAddress, amountZero, nonce, gasPrice, gasLimit, chainId, data); + + byte[][] hashesForSign = new byte[1][]; + byte[] for_hash = tx.getRawHash(); + hashesForSign[0] = for_hash; + + byte[] signFromCard = null; + try { + signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, flag, null, issuer).getTLV(TLV.Tag.TAG_Signature).Value; + // TODO slice signFromCard to hashes.length parts + } catch (Exception ex) { + Log.e("ETH", ex.getMessage()); + return null; + } + + LastSignStorage.setLastSignDate(mCard.getWallet(), new Date()); + + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); + s = CryptoUtil.toCanonicalised(s); + + boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey); + + if(!f) + { + Log.e("ETH-CHECK", "Sign Failed."); + } + + tx.signature = new ECDSASignature_ETH(r, s); + int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey); + if (v != 27 && v != 28) { + Log.e("ETH", "invalid v"); + return null; + } + tx.signature.v = (byte) v; + Log.e("ETH_v", String.valueOf(v)); + + byte[] realTX = tx.getEncoded(); + return realTX; + } +} diff --git a/app/src/main/java/com/tangem/wallet/Transaction.java b/app/src/main/java/com/tangem/wallet/Transaction.java index 3c755b03c0..9cab3f0182 100644 --- a/app/src/main/java/com/tangem/wallet/Transaction.java +++ b/app/src/main/java/com/tangem/wallet/Transaction.java @@ -1,632 +1,632 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 29.09.2017. - */ - -import org.spongycastle.jcajce.provider.symmetric.ARC4; - -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Stack; - -@SuppressWarnings("WeakerAccess") -public final class Transaction { - public final int version; - public final Input[] inputs; - public final Output[] outputs; - public final int lockTime; - - public Transaction(byte[] rawBytes) throws BitcoinException { - if (rawBytes == null) { - throw new BitcoinException(BitcoinException.ERR_NO_INPUT, "empty input"); - } - BitcoinInputStream bais = null; - try { - bais = new BitcoinInputStream(rawBytes); - version = bais.readInt32(); - if (version != 1 && version != 2 && version != 3) { - throw new BitcoinException(BitcoinException.ERR_UNSUPPORTED, "Unsupported TX version", version); - } - - - int inputsCount = 0; - int first = bais.readByte(); - if(first == 0) - { - int skip = bais.readByte(); - inputsCount = bais.readByte(); - } - else - { - inputsCount = first; - } - //int inputsCount = (int) bais.readVarInt(); TODO: - inputs = new Input[inputsCount]; - for (int i = 0; i < inputsCount; i++) { - OutPoint outPoint = new OutPoint(BTCUtils.reverse(bais.readChars(32)), bais.readInt32()); - byte[] script = bais.readChars((int) bais.readVarInt()); - int sequence = bais.readInt32(); - inputs[i] = new Input(outPoint, new Script(script), sequence); - } - int outputsCount = (int) bais.readVarInt(); - outputs = new Output[outputsCount]; - for (int i = 0; i < outputsCount; i++) { - long value = bais.readInt64(); - long scriptSize = bais.readVarInt(); - if (scriptSize < 0 || scriptSize > 10_000_000) { - throw new BitcoinException(BitcoinException.ERR_BAD_FORMAT, "Script size for output " + i + - " is strange (" + scriptSize + " bytes)."); - } - byte[] script = bais.readChars((int) scriptSize); - outputs[i] = new Output(value, new Script(script)); - } - lockTime = bais.readInt32(); - } catch (EOFException e) { - throw new BitcoinException(BitcoinException.ERR_BAD_FORMAT, "TX incomplete"); - } catch (IOException e) { - throw new IllegalArgumentException("Unable to read TX"); - } catch (Error e) { - throw new IllegalArgumentException("Unable to read TX: " + e); - } finally { - if (bais != null) { - try { - bais.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - public Transaction(Input[] inputs, Output[] outputs, int lockTime) { - this.version = 1; - this.inputs = inputs; - this.outputs = outputs; - this.lockTime = lockTime; - } - - public byte[] getBytes() { - BitcoinOutputStream baos = new BitcoinOutputStream(); - try { - baos.writeInt32(version); - baos.writeVarInt(inputs.length); - for (Input input : inputs) { - baos.write(BTCUtils.reverse(input.outPoint.hash)); - baos.writeInt32(input.outPoint.index); - int scriptLen = input.script == null ? 0 : input.script.bytes.length; - baos.writeVarInt(scriptLen); - if (scriptLen > 0) { - baos.write(input.script.bytes); - } - baos.writeInt32(input.sequence); - } - baos.writeVarInt(outputs.length); - for (Output output : outputs) { - baos.writeInt64(output.value); - int scriptLen = output.script == null ? 0 : output.script.bytes.length; - baos.writeVarInt(scriptLen); - if (scriptLen > 0) { - baos.write(output.script.bytes); - } - } - baos.writeInt32(lockTime); - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - baos.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return baos.toByteArray(); - - } - - @Override - public String toString() { - return "{" + - "\n\"inputs\":\n" + printAsJsonArray(inputs) + - ",\n\"outputs\":\n" + printAsJsonArray(outputs) + - ",\n\"lockTime\":\"" + lockTime + "\"}\n"; - } - - private String printAsJsonArray(Object[] a) { - if (a == null) { - return "null"; - } - if (a.length == 0) { - return "[]"; - } - int iMax = a.length - 1; - StringBuilder sb = new StringBuilder(); - sb.append('['); - for (int i = 0; ; i++) { - sb.append(String.valueOf(a[i])); - if (i == iMax) - return sb.append(']').toString(); - sb.append(",\n"); - } - } - - public static class Input { - public final OutPoint outPoint; - public final Script script; - public final int sequence; - - public Input(OutPoint outPoint, Script script, int sequence) { - this.outPoint = outPoint; - this.script = script; - this.sequence = sequence; - } - - @Override - public String toString() { - return "{\n\"outPoint\":" + outPoint + ",\n\"script\":\"" + script + "\",\n\"sequence\":\"" + Integer.toHexString(sequence) + "\"\n}\n"; - } - } - - public static class OutPoint { - public final byte[] hash;//32-byte hash of the transaction from which we want to redeem an output - public final int index;//Four-byte field denoting the output index we want to redeem from the transaction with the above hash (output number 2 = output index 1) - - public OutPoint(byte[] hash, int index) { - this.hash = hash; - this.index = index; - } - - @Override - public String toString() { - return "{" + "\"hash\":\"" + BTCUtils.toHex(hash) + "\", \"index\":\"" + index + "\"}"; - } - } - - public static class Output { - public final long value; - public final Script script; - - public Output(long value, Script script) { - this.value = value; - this.script = script; - } - - @Override - public String toString() { - return "{\n\"value\":\"" + value * 1e-8 + "\",\"script\":\"" + script + "\"\n}"; - } - } - - public static final class Script { - - public static class ScriptInvalidException extends Exception { - public ScriptInvalidException() { - } - - public ScriptInvalidException(String s) { - super(s); - } - } - - public static final byte OP_FALSE = 0; - public static final byte OP_TRUE = 0x51; - public static final byte OP_PUSHDATA1 = 0x4c; - public static final byte OP_PUSHDATA2 = 0x4d; - public static final byte OP_PUSHDATA4 = 0x4e; - public static final byte OP_DUP = 0x76;//Duplicates the top stack item. - public static final byte OP_DROP = 0x75; - public static final byte OP_HASH160 = (byte) 0xA9;//The input is hashed twice: first with SHA-256 and then with RIPEMD-160. - public static final byte OP_VERIFY = 0x69;//Marks transaction as invalid if top stack value is not true. True is removed, but false is not. - public static final byte OP_EQUAL = (byte) 0x87;//Returns 1 if the inputs are exactly equal, 0 otherwise. - public static final byte OP_EQUALVERIFY = (byte) 0x88;//Same as OP_EQUAL, but runs OP_VERIFY afterward. - public static final byte OP_CHECKSIG = (byte) 0xAC;//The entire transaction's outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. - public static final byte OP_CHECKSIGVERIFY = (byte) 0xAD; - public static final byte OP_NOP = 0x61; - - public static final byte SIGHASH_ALL = 1; - - public final byte[] bytes; - - public Script(byte[] rawBytes) { - bytes = rawBytes; - } - - public Script(byte[] data1, byte[] data2) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(data1.length + data2.length + 2); - try { - writeBytes(data1, baos); - writeBytes(data2, baos); - baos.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - bytes = baos.toByteArray(); - } - - private static void writeBytes(byte[] data, ByteArrayOutputStream baos) throws IOException { - if (data.length < OP_PUSHDATA1) { - baos.write(data.length); - } else if (data.length < 0xff) { - baos.write(OP_PUSHDATA1); - baos.write(data.length); - } else if (data.length < 0xffff) { - baos.write(OP_PUSHDATA2); - baos.write(data.length & 0xff); - baos.write((data.length >> 8) & 0xff); - } else { - baos.write(OP_PUSHDATA4); - baos.write(data.length & 0xff); - baos.write((data.length >> 8) & 0xff); - baos.write((data.length >> 16) & 0xff); - baos.write((data.length >>> 24) & 0xff); - } - baos.write(data); - } - - public void run(Stack stack) throws ScriptInvalidException { - run(0, null, stack); - } - - public void run(int inputIndex, Transaction tx, Stack stack) throws ScriptInvalidException { - for (int pos = 0; pos < bytes.length; pos++) { - switch (bytes[pos]) { - case OP_NOP: - break; - case OP_DROP: - if (stack.isEmpty()) { - throw new IllegalArgumentException("stack empty on OP_DROP"); - } - stack.pop(); - break; - case OP_DUP: - if (stack.isEmpty()) { - throw new IllegalArgumentException("stack empty on OP_DUP"); - } - stack.push(stack.peek()); - break; - case OP_HASH160: - if (stack.isEmpty()) { - throw new IllegalArgumentException("stack empty on OP_HASH160"); - } - stack.push(CryptoUtil.sha256ripemd160(stack.pop())); - break; - case OP_EQUAL: - case OP_EQUALVERIFY: - if (stack.size() < 2) { - throw new IllegalArgumentException("not enough elements to perform OP_EQUAL"); - } - stack.push(new byte[]{(byte) (Arrays.equals(stack.pop(), stack.pop()) ? 1 : 0)}); - if (bytes[pos] == OP_EQUALVERIFY) { - if (verifyFails(stack)) { - throw new ScriptInvalidException("wrong address"); - } - } - break; - case OP_VERIFY: - if (verifyFails(stack)) { - throw new ScriptInvalidException(); - } - break; - case OP_CHECKSIG: - case OP_CHECKSIGVERIFY: - byte[] publicKey = stack.pop(); - byte[] signatureAndHashType = stack.pop(); - if (signatureAndHashType[signatureAndHashType.length - 1] != SIGHASH_ALL) { - throw new IllegalArgumentException("I cannot check this sig type: " + signatureAndHashType[signatureAndHashType.length - 1]); - } - byte[] signature = new byte[signatureAndHashType.length - 1]; - System.arraycopy(signatureAndHashType, 0, signature, 0, signature.length); - byte[] hash = hashTransaction(inputIndex, bytes, tx); - //boolean valid = BTCUtils.verify(publicKey, signature, hash); - if (bytes[pos] == OP_CHECKSIG) { - stack.push(new byte[]{(byte) (1)}); - } else { - if (verifyFails(stack)) { - throw new ScriptInvalidException("Bad signature"); - } - if (!stack.empty()) { - throw new ScriptInvalidException("Bad signature - superfluous scriptSig operations"); - } - } - break; - case OP_FALSE: - stack.push(new byte[]{0}); - break; - case OP_TRUE: - stack.push(new byte[]{1}); - break; - default: - int op = bytes[pos] & 0xff; - int len; - if (op < OP_PUSHDATA1) { - len = op; - byte[] data = new byte[len]; - System.arraycopy(bytes, pos + 1, data, 0, len); - stack.push(data); - pos += data.length; - } else if (op == OP_PUSHDATA1) { - len = bytes[pos + 1] & 0xff; - byte[] data = new byte[len]; - System.arraycopy(bytes, pos + 1, data, 0, len); - stack.push(data); - pos += 1 + data.length; - } else { - throw new IllegalArgumentException("I cannot read this data: " + Integer.toHexString(bytes[pos])); - } - break; - } - } - } - - public static byte[] hashTransaction(int inputIndex, byte[] subscript, Transaction tx) { - Input[] unsignedInputs = new Input[tx.inputs.length]; - for (int i = 0; i < tx.inputs.length; i++) { - Input txInput = tx.inputs[i]; - if (i == inputIndex) { - unsignedInputs[i] = new Input(txInput.outPoint, new Script(subscript), txInput.sequence); - } else { - unsignedInputs[i] = new Input(txInput.outPoint, new Script(new byte[0]), txInput.sequence); - } - } - Transaction unsignedTransaction = new Transaction(unsignedInputs, tx.outputs, tx.lockTime); - return hashTransactionForSigning(unsignedTransaction); - } - - public static byte[] hashTransactionForSigning(Transaction unsignedTransaction) { - byte[] txUnsignedBytes = unsignedTransaction.getBytes(); - BitcoinOutputStream baos = new BitcoinOutputStream(); - try { - baos.write(txUnsignedBytes); - baos.writeInt32(Script.SIGHASH_ALL); - baos.close(); - } catch (Exception e) { - throw new RuntimeException(e); - } - return CryptoUtil.doubleSha256(baos.toByteArray()); - } - - public static boolean verifyFails(Stack stack) { - byte[] input; - boolean valid; - input = stack.pop(); - if (input.length == 0 || (input.length == 1 && input[0] == OP_FALSE)) { - //false - stack.push(new byte[]{OP_FALSE}); - valid = false; - } else { - //true - valid = true; - } - return !valid; - } - - - @Override - public String toString() { - return convertBytesToReadableString(bytes); - } - - //converts something like "OP_DUP OP_HASH160 ba507bae8f1643d2556000ca26b9301b9069dc6b OP_EQUALVERIFY OP_CHECKSIG" into bytes - public static byte[] convertReadableStringToBytes(String readableString) { - String[] tokens = readableString.trim().split("\\s+"); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - for (String token : tokens) { - switch (token) { - case "OP_NOP": - os.write(OP_NOP); - break; - case "OP_DROP": - os.write(OP_DROP); - break; - case "OP_DUP": - os.write(OP_DUP); - break; - case "OP_HASH160": - os.write(OP_HASH160); - break; - case "OP_EQUAL": - os.write(OP_EQUAL); - break; - case "OP_EQUALVERIFY": - os.write(OP_EQUALVERIFY); - break; - case "OP_VERIFY": - os.write(OP_VERIFY); - break; - case "OP_CHECKSIG": - os.write(OP_CHECKSIG); - break; - case "OP_CHECKSIGVERIFY": - os.write(OP_CHECKSIGVERIFY); - break; - case "OP_FALSE": - os.write(OP_FALSE); - break; - case "OP_TRUE": - os.write(OP_TRUE); - break; - default: - if (token.startsWith("OP_")) { - throw new IllegalArgumentException("I don't know this operation: " + token); - } - byte[] data = BTCUtils.fromHex(token); - if (data == null) { - throw new IllegalArgumentException("I don't know what's this: " + token); - } - if (data.length < OP_PUSHDATA1) { - os.write(data.length); - try { - os.write(data); - } catch (IOException e) { - throw new RuntimeException("ByteArrayOutputStream behaves weird: " + e); - } - } else if (data.length <= 255) { - os.write(OP_PUSHDATA1); - os.write(data.length); - try { - os.write(data); - } catch (IOException e) { - throw new RuntimeException("ByteArrayOutputStream behaves weird: " + e); - } - } else { - throw new IllegalArgumentException("OP_PUSHDATA2 & OP_PUSHDATA4 are not supported"); - } - break; - } - } - try { - os.close(); - } catch (IOException e) { - e.printStackTrace(); - } - return os.toByteArray(); - } - - public static String convertBytesToReadableString(byte[] bytes) { - StringBuilder sb = new StringBuilder(); - for (int pos = 0; pos < bytes.length; pos++) { - if (sb.length() > 0) { - sb.append(' '); - } - switch (bytes[pos]) { - case OP_NOP: - sb.append("OP_NOP"); - break; - case OP_DROP: - sb.append("OP_DROP"); - break; - case OP_DUP: - sb.append("OP_DUP"); - break; - case OP_HASH160: - sb.append("OP_HASH160"); - break; - case OP_EQUAL: - sb.append("OP_EQUAL"); - break; - case OP_EQUALVERIFY: - sb.append("OP_EQUALVERIFY"); - break; - case OP_VERIFY: - sb.append("OP_VERIFY"); - break; - case OP_CHECKSIG: - sb.append("OP_CHECKSIG"); - break; - case OP_CHECKSIGVERIFY: - sb.append("OP_CHECKSIGVERIFY"); - break; - case OP_FALSE: - sb.append("OP_FALSE"); - break; - case OP_TRUE: - sb.append("OP_TRUE"); - break; - default: - int op = bytes[pos] & 0xff; - int len; - if (op < OP_PUSHDATA1) { - len = op; - byte[] data = new byte[len]; - System.arraycopy(bytes, pos + 1, data, 0, len); - sb.append(BTCUtils.toHex(data)); - pos += data.length; - } else if (op == OP_PUSHDATA1) { - len = bytes[pos + 1] & 0xff; - byte[] data = new byte[len]; - System.arraycopy(bytes, pos + 1, data, 0, len);//FIXME I suspect there is off by one error... - sb.append(BTCUtils.toHex(data)); - pos += 1 + data.length; - } else { - throw new IllegalArgumentException("I cannot read this data: " + Integer.toHexString(bytes[pos]) + " at " + pos); - } - break; - } - } - return sb.toString(); - } - - @Override - public boolean equals(Object o) { - return this == o || !(o == null || getClass() != o.getClass()) && Arrays.equals(bytes, ((Script) o).bytes); - } - - @Override - public int hashCode() { - return Arrays.hashCode(bytes); - } - - public static Script buildOutput(String address) throws BitcoinException { - //noinspection TryWithIdenticalCatches - byte[] addressWithCheckSumAndNetworkCode = Base58.decodeBase58(address); - if (addressWithCheckSumAndNetworkCode[0] == 0 || addressWithCheckSumAndNetworkCode[0] == 111) { - return buildOutputP2H(address); - } - - if(addressWithCheckSumAndNetworkCode[0] == 5 || addressWithCheckSumAndNetworkCode[0] == (byte)0xc4) { - return buildOutputP2SH(address); - } - - throw new BitcoinException(BitcoinException.ERR_UNSUPPORTED, "Unknown address type", address); - } - public static Script buildOutputP2SH(String address) throws BitcoinException { - try { - byte[] addressWithCheckSumAndNetworkCode = Base58.decodeBase58(address); - if (addressWithCheckSumAndNetworkCode[0] != 5 && addressWithCheckSumAndNetworkCode[0] != (byte)0xc4) { - throw new BitcoinException(BitcoinException.ERR_UNSUPPORTED, "Unknown address type", address); - } - - byte[] bareAddress = new byte[20]; - System.arraycopy(addressWithCheckSumAndNetworkCode, 1, bareAddress, 0, bareAddress.length); - - ByteArrayOutputStream buf = new ByteArrayOutputStream(23); - buf.write(OP_HASH160); - writeBytes(bareAddress, buf); - buf.write(OP_EQUAL); - return new Script(buf.toByteArray()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - - public static Script buildOutputP2H(String address) throws BitcoinException { - //noinspection TryWithIdenticalCatches - try { - byte[] addressWithCheckSumAndNetworkCode = Base58.decodeBase58(address); - if (addressWithCheckSumAndNetworkCode[0] != 0 && addressWithCheckSumAndNetworkCode[0] != 111) { - throw new BitcoinException(BitcoinException.ERR_UNSUPPORTED, "Unknown address type", address); - } - - byte[] bareAddress = new byte[20]; - System.arraycopy(addressWithCheckSumAndNetworkCode, 1, bareAddress, 0, bareAddress.length); - - MessageDigest digestSha = MessageDigest.getInstance("SHA-256"); - digestSha.update(addressWithCheckSumAndNetworkCode, 0, addressWithCheckSumAndNetworkCode.length - 4); - - byte[] calculatedDigest = digestSha.digest(digestSha.digest()); - for (int i = 0; i < 4; i++) { - if (calculatedDigest[i] != addressWithCheckSumAndNetworkCode[addressWithCheckSumAndNetworkCode.length - 4 + i]) { - throw new BitcoinException(BitcoinException.ERR_BAD_FORMAT, "Bad address", address); - } - } - - ByteArrayOutputStream buf = new ByteArrayOutputStream(25); - buf.write(OP_DUP); - buf.write(OP_HASH160); - writeBytes(bareAddress, buf); - buf.write(OP_EQUALVERIFY); - buf.write(OP_CHECKSIG); - return new Script(buf.toByteArray()); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } -} +package com.tangem.wallet; + +/** + * Created by Ilia on 29.09.2017. + */ + +import org.spongycastle.jcajce.provider.symmetric.ARC4; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Stack; + +@SuppressWarnings("WeakerAccess") +public final class Transaction { + public final int version; + public final Input[] inputs; + public final Output[] outputs; + public final int lockTime; + + public Transaction(byte[] rawBytes) throws BitcoinException { + if (rawBytes == null) { + throw new BitcoinException(BitcoinException.ERR_NO_INPUT, "empty input"); + } + BitcoinInputStream bais = null; + try { + bais = new BitcoinInputStream(rawBytes); + version = bais.readInt32(); + if (version != 1 && version != 2 && version != 3) { + throw new BitcoinException(BitcoinException.ERR_UNSUPPORTED, "Unsupported TX version", version); + } + + + int inputsCount = 0; + int first = bais.readByte(); + if(first == 0) + { + int skip = bais.readByte(); + inputsCount = bais.readByte(); + } + else + { + inputsCount = first; + } + //int inputsCount = (int) bais.readVarInt(); TODO: + inputs = new Input[inputsCount]; + for (int i = 0; i < inputsCount; i++) { + OutPoint outPoint = new OutPoint(BTCUtils.reverse(bais.readChars(32)), bais.readInt32()); + byte[] script = bais.readChars((int) bais.readVarInt()); + int sequence = bais.readInt32(); + inputs[i] = new Input(outPoint, new Script(script), sequence); + } + int outputsCount = (int) bais.readVarInt(); + outputs = new Output[outputsCount]; + for (int i = 0; i < outputsCount; i++) { + long value = bais.readInt64(); + long scriptSize = bais.readVarInt(); + if (scriptSize < 0 || scriptSize > 10_000_000) { + throw new BitcoinException(BitcoinException.ERR_BAD_FORMAT, "Script size for output " + i + + " is strange (" + scriptSize + " bytes)."); + } + byte[] script = bais.readChars((int) scriptSize); + outputs[i] = new Output(value, new Script(script)); + } + lockTime = bais.readInt32(); + } catch (EOFException e) { + throw new BitcoinException(BitcoinException.ERR_BAD_FORMAT, "TX incomplete"); + } catch (IOException e) { + throw new IllegalArgumentException("Unable to read TX"); + } catch (Error e) { + throw new IllegalArgumentException("Unable to read TX: " + e); + } finally { + if (bais != null) { + try { + bais.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public Transaction(Input[] inputs, Output[] outputs, int lockTime) { + this.version = 1; + this.inputs = inputs; + this.outputs = outputs; + this.lockTime = lockTime; + } + + public byte[] getBytes() { + BitcoinOutputStream baos = new BitcoinOutputStream(); + try { + baos.writeInt32(version); + baos.writeVarInt(inputs.length); + for (Input input : inputs) { + baos.write(BTCUtils.reverse(input.outPoint.hash)); + baos.writeInt32(input.outPoint.index); + int scriptLen = input.script == null ? 0 : input.script.bytes.length; + baos.writeVarInt(scriptLen); + if (scriptLen > 0) { + baos.write(input.script.bytes); + } + baos.writeInt32(input.sequence); + } + baos.writeVarInt(outputs.length); + for (Output output : outputs) { + baos.writeInt64(output.value); + int scriptLen = output.script == null ? 0 : output.script.bytes.length; + baos.writeVarInt(scriptLen); + if (scriptLen > 0) { + baos.write(output.script.bytes); + } + } + baos.writeInt32(lockTime); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + baos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return baos.toByteArray(); + + } + + @Override + public String toString() { + return "{" + + "\n\"inputs\":\n" + printAsJsonArray(inputs) + + ",\n\"outputs\":\n" + printAsJsonArray(outputs) + + ",\n\"lockTime\":\"" + lockTime + "\"}\n"; + } + + private String printAsJsonArray(Object[] a) { + if (a == null) { + return "null"; + } + if (a.length == 0) { + return "[]"; + } + int iMax = a.length - 1; + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0; ; i++) { + sb.append(String.valueOf(a[i])); + if (i == iMax) + return sb.append(']').toString(); + sb.append(",\n"); + } + } + + public static class Input { + public final OutPoint outPoint; + public final Script script; + public final int sequence; + + public Input(OutPoint outPoint, Script script, int sequence) { + this.outPoint = outPoint; + this.script = script; + this.sequence = sequence; + } + + @Override + public String toString() { + return "{\n\"outPoint\":" + outPoint + ",\n\"script\":\"" + script + "\",\n\"sequence\":\"" + Integer.toHexString(sequence) + "\"\n}\n"; + } + } + + public static class OutPoint { + public final byte[] hash;//32-byte hash of the transaction from which we want to redeem an output + public final int index;//Four-byte field denoting the output index we want to redeem from the transaction with the above hash (output number 2 = output index 1) + + public OutPoint(byte[] hash, int index) { + this.hash = hash; + this.index = index; + } + + @Override + public String toString() { + return "{" + "\"hash\":\"" + BTCUtils.toHex(hash) + "\", \"index\":\"" + index + "\"}"; + } + } + + public static class Output { + public final long value; + public final Script script; + + public Output(long value, Script script) { + this.value = value; + this.script = script; + } + + @Override + public String toString() { + return "{\n\"value\":\"" + value * 1e-8 + "\",\"script\":\"" + script + "\"\n}"; + } + } + + public static final class Script { + + public static class ScriptInvalidException extends Exception { + public ScriptInvalidException() { + } + + public ScriptInvalidException(String s) { + super(s); + } + } + + public static final byte OP_FALSE = 0; + public static final byte OP_TRUE = 0x51; + public static final byte OP_PUSHDATA1 = 0x4c; + public static final byte OP_PUSHDATA2 = 0x4d; + public static final byte OP_PUSHDATA4 = 0x4e; + public static final byte OP_DUP = 0x76;//Duplicates the top stack item. + public static final byte OP_DROP = 0x75; + public static final byte OP_HASH160 = (byte) 0xA9;//The input is hashed twice: first with SHA-256 and then with RIPEMD-160. + public static final byte OP_VERIFY = 0x69;//Marks transaction as invalid if top stack value is not true. True is removed, but false is not. + public static final byte OP_EQUAL = (byte) 0x87;//Returns 1 if the inputs are exactly equal, 0 otherwise. + public static final byte OP_EQUALVERIFY = (byte) 0x88;//Same as OP_EQUAL, but runs OP_VERIFY afterward. + public static final byte OP_CHECKSIG = (byte) 0xAC;//The entire transaction's outputs, inputs, and script (from the most recently-executed OP_CODESEPARATOR to the end) are hashed. The signature used by OP_CHECKSIG must be a valid signature for this hash and public key. If it is, 1 is returned, 0 otherwise. + public static final byte OP_CHECKSIGVERIFY = (byte) 0xAD; + public static final byte OP_NOP = 0x61; + + public static final byte SIGHASH_ALL = 1; + + public final byte[] bytes; + + public Script(byte[] rawBytes) { + bytes = rawBytes; + } + + public Script(byte[] data1, byte[] data2) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(data1.length + data2.length + 2); + try { + writeBytes(data1, baos); + writeBytes(data2, baos); + baos.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + bytes = baos.toByteArray(); + } + + private static void writeBytes(byte[] data, ByteArrayOutputStream baos) throws IOException { + if (data.length < OP_PUSHDATA1) { + baos.write(data.length); + } else if (data.length < 0xff) { + baos.write(OP_PUSHDATA1); + baos.write(data.length); + } else if (data.length < 0xffff) { + baos.write(OP_PUSHDATA2); + baos.write(data.length & 0xff); + baos.write((data.length >> 8) & 0xff); + } else { + baos.write(OP_PUSHDATA4); + baos.write(data.length & 0xff); + baos.write((data.length >> 8) & 0xff); + baos.write((data.length >> 16) & 0xff); + baos.write((data.length >>> 24) & 0xff); + } + baos.write(data); + } + + public void run(Stack stack) throws ScriptInvalidException { + run(0, null, stack); + } + + public void run(int inputIndex, Transaction tx, Stack stack) throws ScriptInvalidException { + for (int pos = 0; pos < bytes.length; pos++) { + switch (bytes[pos]) { + case OP_NOP: + break; + case OP_DROP: + if (stack.isEmpty()) { + throw new IllegalArgumentException("stack empty on OP_DROP"); + } + stack.pop(); + break; + case OP_DUP: + if (stack.isEmpty()) { + throw new IllegalArgumentException("stack empty on OP_DUP"); + } + stack.push(stack.peek()); + break; + case OP_HASH160: + if (stack.isEmpty()) { + throw new IllegalArgumentException("stack empty on OP_HASH160"); + } + stack.push(CryptoUtil.sha256ripemd160(stack.pop())); + break; + case OP_EQUAL: + case OP_EQUALVERIFY: + if (stack.size() < 2) { + throw new IllegalArgumentException("not enough elements to perform OP_EQUAL"); + } + stack.push(new byte[]{(byte) (Arrays.equals(stack.pop(), stack.pop()) ? 1 : 0)}); + if (bytes[pos] == OP_EQUALVERIFY) { + if (verifyFails(stack)) { + throw new ScriptInvalidException("wrong address"); + } + } + break; + case OP_VERIFY: + if (verifyFails(stack)) { + throw new ScriptInvalidException(); + } + break; + case OP_CHECKSIG: + case OP_CHECKSIGVERIFY: + byte[] publicKey = stack.pop(); + byte[] signatureAndHashType = stack.pop(); + if (signatureAndHashType[signatureAndHashType.length - 1] != SIGHASH_ALL) { + throw new IllegalArgumentException("I cannot check this sig type: " + signatureAndHashType[signatureAndHashType.length - 1]); + } + byte[] signature = new byte[signatureAndHashType.length - 1]; + System.arraycopy(signatureAndHashType, 0, signature, 0, signature.length); + byte[] hash = hashTransaction(inputIndex, bytes, tx); + //boolean valid = BTCUtils.verify(publicKey, signature, hash); + if (bytes[pos] == OP_CHECKSIG) { + stack.push(new byte[]{(byte) (1)}); + } else { + if (verifyFails(stack)) { + throw new ScriptInvalidException("Bad signature"); + } + if (!stack.empty()) { + throw new ScriptInvalidException("Bad signature - superfluous scriptSig operations"); + } + } + break; + case OP_FALSE: + stack.push(new byte[]{0}); + break; + case OP_TRUE: + stack.push(new byte[]{1}); + break; + default: + int op = bytes[pos] & 0xff; + int len; + if (op < OP_PUSHDATA1) { + len = op; + byte[] data = new byte[len]; + System.arraycopy(bytes, pos + 1, data, 0, len); + stack.push(data); + pos += data.length; + } else if (op == OP_PUSHDATA1) { + len = bytes[pos + 1] & 0xff; + byte[] data = new byte[len]; + System.arraycopy(bytes, pos + 1, data, 0, len); + stack.push(data); + pos += 1 + data.length; + } else { + throw new IllegalArgumentException("I cannot read this data: " + Integer.toHexString(bytes[pos])); + } + break; + } + } + } + + public static byte[] hashTransaction(int inputIndex, byte[] subscript, Transaction tx) { + Input[] unsignedInputs = new Input[tx.inputs.length]; + for (int i = 0; i < tx.inputs.length; i++) { + Input txInput = tx.inputs[i]; + if (i == inputIndex) { + unsignedInputs[i] = new Input(txInput.outPoint, new Script(subscript), txInput.sequence); + } else { + unsignedInputs[i] = new Input(txInput.outPoint, new Script(new byte[0]), txInput.sequence); + } + } + Transaction unsignedTransaction = new Transaction(unsignedInputs, tx.outputs, tx.lockTime); + return hashTransactionForSigning(unsignedTransaction); + } + + public static byte[] hashTransactionForSigning(Transaction unsignedTransaction) { + byte[] txUnsignedBytes = unsignedTransaction.getBytes(); + BitcoinOutputStream baos = new BitcoinOutputStream(); + try { + baos.write(txUnsignedBytes); + baos.writeInt32(Script.SIGHASH_ALL); + baos.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return CryptoUtil.doubleSha256(baos.toByteArray()); + } + + public static boolean verifyFails(Stack stack) { + byte[] input; + boolean valid; + input = stack.pop(); + if (input.length == 0 || (input.length == 1 && input[0] == OP_FALSE)) { + //false + stack.push(new byte[]{OP_FALSE}); + valid = false; + } else { + //true + valid = true; + } + return !valid; + } + + + @Override + public String toString() { + return convertBytesToReadableString(bytes); + } + + //converts something like "OP_DUP OP_HASH160 ba507bae8f1643d2556000ca26b9301b9069dc6b OP_EQUALVERIFY OP_CHECKSIG" into bytes + public static byte[] convertReadableStringToBytes(String readableString) { + String[] tokens = readableString.trim().split("\\s+"); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + for (String token : tokens) { + switch (token) { + case "OP_NOP": + os.write(OP_NOP); + break; + case "OP_DROP": + os.write(OP_DROP); + break; + case "OP_DUP": + os.write(OP_DUP); + break; + case "OP_HASH160": + os.write(OP_HASH160); + break; + case "OP_EQUAL": + os.write(OP_EQUAL); + break; + case "OP_EQUALVERIFY": + os.write(OP_EQUALVERIFY); + break; + case "OP_VERIFY": + os.write(OP_VERIFY); + break; + case "OP_CHECKSIG": + os.write(OP_CHECKSIG); + break; + case "OP_CHECKSIGVERIFY": + os.write(OP_CHECKSIGVERIFY); + break; + case "OP_FALSE": + os.write(OP_FALSE); + break; + case "OP_TRUE": + os.write(OP_TRUE); + break; + default: + if (token.startsWith("OP_")) { + throw new IllegalArgumentException("I don't know this operation: " + token); + } + byte[] data = BTCUtils.fromHex(token); + if (data == null) { + throw new IllegalArgumentException("I don't know what's this: " + token); + } + if (data.length < OP_PUSHDATA1) { + os.write(data.length); + try { + os.write(data); + } catch (IOException e) { + throw new RuntimeException("ByteArrayOutputStream behaves weird: " + e); + } + } else if (data.length <= 255) { + os.write(OP_PUSHDATA1); + os.write(data.length); + try { + os.write(data); + } catch (IOException e) { + throw new RuntimeException("ByteArrayOutputStream behaves weird: " + e); + } + } else { + throw new IllegalArgumentException("OP_PUSHDATA2 & OP_PUSHDATA4 are not supported"); + } + break; + } + } + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return os.toByteArray(); + } + + public static String convertBytesToReadableString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (int pos = 0; pos < bytes.length; pos++) { + if (sb.length() > 0) { + sb.append(' '); + } + switch (bytes[pos]) { + case OP_NOP: + sb.append("OP_NOP"); + break; + case OP_DROP: + sb.append("OP_DROP"); + break; + case OP_DUP: + sb.append("OP_DUP"); + break; + case OP_HASH160: + sb.append("OP_HASH160"); + break; + case OP_EQUAL: + sb.append("OP_EQUAL"); + break; + case OP_EQUALVERIFY: + sb.append("OP_EQUALVERIFY"); + break; + case OP_VERIFY: + sb.append("OP_VERIFY"); + break; + case OP_CHECKSIG: + sb.append("OP_CHECKSIG"); + break; + case OP_CHECKSIGVERIFY: + sb.append("OP_CHECKSIGVERIFY"); + break; + case OP_FALSE: + sb.append("OP_FALSE"); + break; + case OP_TRUE: + sb.append("OP_TRUE"); + break; + default: + int op = bytes[pos] & 0xff; + int len; + if (op < OP_PUSHDATA1) { + len = op; + byte[] data = new byte[len]; + System.arraycopy(bytes, pos + 1, data, 0, len); + sb.append(BTCUtils.toHex(data)); + pos += data.length; + } else if (op == OP_PUSHDATA1) { + len = bytes[pos + 1] & 0xff; + byte[] data = new byte[len]; + System.arraycopy(bytes, pos + 1, data, 0, len);//FIXME I suspect there is off by one error... + sb.append(BTCUtils.toHex(data)); + pos += 1 + data.length; + } else { + throw new IllegalArgumentException("I cannot read this data: " + Integer.toHexString(bytes[pos]) + " at " + pos); + } + break; + } + } + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + return this == o || !(o == null || getClass() != o.getClass()) && Arrays.equals(bytes, ((Script) o).bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(bytes); + } + + public static Script buildOutput(String address) throws BitcoinException { + //noinspection TryWithIdenticalCatches + byte[] addressWithCheckSumAndNetworkCode = Base58.decodeBase58(address); + if (addressWithCheckSumAndNetworkCode[0] == 0 || addressWithCheckSumAndNetworkCode[0] == 111) { + return buildOutputP2H(address); + } + + if(addressWithCheckSumAndNetworkCode[0] == 5 || addressWithCheckSumAndNetworkCode[0] == (byte)0xc4) { + return buildOutputP2SH(address); + } + + throw new BitcoinException(BitcoinException.ERR_UNSUPPORTED, "Unknown address type", address); + } + public static Script buildOutputP2SH(String address) throws BitcoinException { + try { + byte[] addressWithCheckSumAndNetworkCode = Base58.decodeBase58(address); + if (addressWithCheckSumAndNetworkCode[0] != 5 && addressWithCheckSumAndNetworkCode[0] != (byte)0xc4) { + throw new BitcoinException(BitcoinException.ERR_UNSUPPORTED, "Unknown address type", address); + } + + byte[] bareAddress = new byte[20]; + System.arraycopy(addressWithCheckSumAndNetworkCode, 1, bareAddress, 0, bareAddress.length); + + ByteArrayOutputStream buf = new ByteArrayOutputStream(23); + buf.write(OP_HASH160); + writeBytes(bareAddress, buf); + buf.write(OP_EQUAL); + return new Script(buf.toByteArray()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + public static Script buildOutputP2H(String address) throws BitcoinException { + //noinspection TryWithIdenticalCatches + try { + byte[] addressWithCheckSumAndNetworkCode = Base58.decodeBase58(address); + if (addressWithCheckSumAndNetworkCode[0] != 0 && addressWithCheckSumAndNetworkCode[0] != 111) { + throw new BitcoinException(BitcoinException.ERR_UNSUPPORTED, "Unknown address type", address); + } + + byte[] bareAddress = new byte[20]; + System.arraycopy(addressWithCheckSumAndNetworkCode, 1, bareAddress, 0, bareAddress.length); + + MessageDigest digestSha = MessageDigest.getInstance("SHA-256"); + digestSha.update(addressWithCheckSumAndNetworkCode, 0, addressWithCheckSumAndNetworkCode.length - 4); + + byte[] calculatedDigest = digestSha.digest(digestSha.digest()); + for (int i = 0; i < 4; i++) { + if (calculatedDigest[i] != addressWithCheckSumAndNetworkCode[addressWithCheckSumAndNetworkCode.length - 4 + i]) { + throw new BitcoinException(BitcoinException.ERR_BAD_FORMAT, "Bad address", address); + } + } + + ByteArrayOutputStream buf = new ByteArrayOutputStream(25); + buf.write(OP_DUP); + buf.write(OP_HASH160); + writeBytes(bareAddress, buf); + buf.write(OP_EQUALVERIFY); + buf.write(OP_CHECKSIG); + return new Script(buf.toByteArray()); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/app/src/main/java/com/tangem/wallet/UnspentOutputInfo.java b/app/src/main/java/com/tangem/wallet/UnspentOutputInfo.java index a4462b69d6..fdf0788adc 100644 --- a/app/src/main/java/com/tangem/wallet/UnspentOutputInfo.java +++ b/app/src/main/java/com/tangem/wallet/UnspentOutputInfo.java @@ -1,28 +1,28 @@ -package com.tangem.wallet; - -/** - * Created by Ilia on 29.09.2017. - */ - -@SuppressWarnings("WeakerAccess") -public class UnspentOutputInfo { - public final byte[] txHash; - public final Transaction.Script script; - public final long value; - public final int outputIndex; - public final long confirmations; - public String txHashForBuild; - public byte[] scriptForBuild; - public byte[] bodyDoubleHash; - public byte[] bodyHash; - - public UnspentOutputInfo(byte[] txHash, Transaction.Script script, long value, int outputIndex, long confirmations, String hashForBuild, byte[] sign) { - this.txHash = txHash; - this.script = script; - this.value = value; - this.outputIndex = outputIndex; - this.confirmations = confirmations; - this.txHashForBuild = hashForBuild; - this.scriptForBuild = sign; - } +package com.tangem.wallet; + +/** + * Created by Ilia on 29.09.2017. + */ + +@SuppressWarnings("WeakerAccess") +public class UnspentOutputInfo { + public final byte[] txHash; + public final Transaction.Script script; + public final long value; + public final int outputIndex; + public final long confirmations; + public String txHashForBuild; + public byte[] scriptForBuild; + public byte[] bodyDoubleHash; + public byte[] bodyHash; + + public UnspentOutputInfo(byte[] txHash, Transaction.Script script, long value, int outputIndex, long confirmations, String hashForBuild, byte[] sign) { + this.txHash = txHash; + this.script = script; + this.value = value; + this.outputIndex = outputIndex; + this.confirmations = confirmations; + this.txHashForBuild = hashForBuild; + this.scriptForBuild = sign; + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/VerifyCardActivity.java b/app/src/main/java/com/tangem/wallet/VerifyCardActivity.java index 30a8153057..29113fdb47 100644 --- a/app/src/main/java/com/tangem/wallet/VerifyCardActivity.java +++ b/app/src/main/java/com/tangem/wallet/VerifyCardActivity.java @@ -1,28 +1,28 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; - - -public class VerifyCardActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_verify_card); - - MainActivity.commonInit(getApplicationContext()); - } - - @Override - public void onBackPressed() { - //super.onBackPressed(); - VerifyCardActivityFragment verifyCardActivityFragment= (VerifyCardActivityFragment) getSupportFragmentManager().findFragmentById(R.id.verify_card_fragment); - Intent data= verifyCardActivityFragment.prepareResultIntent(); - data.putExtra("modification", "update"); - setResult(Activity.RESULT_OK, data); - finish(); - } -} +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + + +public class VerifyCardActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_verify_card); + + MainActivity.commonInit(getApplicationContext()); + } + + @Override + public void onBackPressed() { + //super.onBackPressed(); + VerifyCardActivityFragment verifyCardActivityFragment= (VerifyCardActivityFragment) getSupportFragmentManager().findFragmentById(R.id.verify_card_fragment); + Intent data= verifyCardActivityFragment.prepareResultIntent(); + data.putExtra("modification", "update"); + setResult(Activity.RESULT_OK, data); + finish(); + } +} diff --git a/app/src/main/java/com/tangem/wallet/VerifyCardActivityFragment.java b/app/src/main/java/com/tangem/wallet/VerifyCardActivityFragment.java index 53f64d5fa1..c04b7912a0 100644 --- a/app/src/main/java/com/tangem/wallet/VerifyCardActivityFragment.java +++ b/app/src/main/java/com/tangem/wallet/VerifyCardActivityFragment.java @@ -1,360 +1,360 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.content.Intent; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.widget.SwipeRefreshLayout; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.tangem.cardReader.NfcManager; - -import java.io.IOException; -import java.util.Timer; -import java.util.TimerTask; - -public class VerifyCardActivityFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener, NfcAdapter.ReaderCallback { - - Tangem_Card mCard; - TextView tvCardID, tvManufacturer, tvRegistrationDate, tvCardIdentity, tvLastSigned, tvRemainingSignatures, tvReusable, tvOk, tvError, tvMessage, - tvIssuer, tvIssuerData, tvFeatures, tvBlockchain, tvSignedTx, tvSigningMethod, tvFirmware, tvWalletIdentity, tvWallet; - ImageView ivBlockchain, ivPIN, ivPIN2orSecurityDelay, ivDeveloperVersion; - SwipeRefreshLayout mSwipeRefreshLayout; - private NfcManager mNfcManager; - - public VerifyCardActivityFragment() { - - } - - public void onRefresh() { - mSwipeRefreshLayout.setRefreshing(false); - } - - @Override - public View onCreateView(final LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - View v = inflater.inflate(R.layout.fragment_verify_card, container, false); - - mNfcManager = new NfcManager(this.getActivity(), this); - - - // SwipeRefreshLayout - mSwipeRefreshLayout = v.findViewById(R.id.swipe_container); - mSwipeRefreshLayout.setOnRefreshListener(this); - - mCard = new Tangem_Card(getActivity().getIntent().getStringExtra("UID")); - mCard.LoadFromBundle(getActivity().getIntent().getExtras().getBundle("Card")); - tvCardID = v.findViewById(R.id.tvCardID); - - tvLastSigned = v.findViewById(R.id.tvLastSigned); - tvRemainingSignatures = v.findViewById(R.id.tvRemainingSignatures); - - tvReusable = v.findViewById(R.id.tvReusable); - - tvManufacturer = v.findViewById(R.id.tvManufacturerInfo); - - tvCardIdentity = v.findViewById(R.id.tvCardIdentity); - - tvRegistrationDate = v.findViewById(R.id.tvCardRegistredDate); - - ivBlockchain = v.findViewById(R.id.imgBlockchain); - ivPIN = v.findViewById(R.id.imgPIN); - ivPIN2orSecurityDelay = v.findViewById(R.id.imgPIN2orSecurityDelay); - ivDeveloperVersion = v.findViewById(R.id.imgDeveloperVersion); - - tvError = v.findViewById(R.id.tvError); - tvMessage = v.findViewById(R.id.tvMessage); - - tvIssuer = v.findViewById(R.id.tvIssuer); - tvIssuerData = v.findViewById(R.id.tvIssuerData); - - tvFirmware = v.findViewById(R.id.tvFirmware); - tvFeatures = v.findViewById(R.id.tvFeatures); - tvBlockchain = v.findViewById(R.id.tvBlockchain); - - tvSignedTx = v.findViewById(R.id.tvSignedTx); - tvSigningMethod = v.findViewById(R.id.tvSigningMethod); - - tvOk = v.findViewById(R.id.tvOk); - if (tvOk != null) { - tvOk.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent data = prepareResultIntent(); - data.putExtra("modification", "update"); - getActivity().setResult(Activity.RESULT_OK, data); - getActivity().finish(); - } - }); - } - - tvWallet = v.findViewById(R.id.tvWallet); - tvWalletIdentity = v.findViewById(R.id.tvWalletIdentity); - - UpdateViews(); - -// if (NeedUpdate) { -// mSwipeRefreshLayout.setRefreshing(true); -// mSwipeRefreshLayout.postDelayed(new Runnable() { -// @Override -// public void run() { -// onRefresh(); -// } -// }, 1000); -// } - return v; - } - - void UpdateViews() { - try { - if (timerHideErrorAndMessage != null) { - timerHideErrorAndMessage.cancel(); - timerHideErrorAndMessage = null; - } - tvCardID.setText(mCard.getCIDDescription()); - - if (mCard.getError() == null || mCard.getError().isEmpty()) { - tvError.setVisibility(View.GONE); - tvError.setText(""); - } else { - tvError.setVisibility(View.VISIBLE); - tvError.setText(mCard.getError()); - } - if (mCard.getMessage() == null || mCard.getMessage().isEmpty()) { - tvMessage.setVisibility(View.GONE); - tvMessage.setText(""); - } else { - tvMessage.setVisibility(View.VISIBLE); - tvMessage.setText(mCard.getMessage()); - } - - tvManufacturer.setText(mCard.getManufacturer().getOfficialName()); - - if (mCard.isManufacturerConfirmed() && mCard.isCardPublicKeyValid()) { - tvCardIdentity.setText("Attested"); - tvCardIdentity.setTextColor(getResources().getColor(R.color.confirmed, getActivity().getTheme())); - } else { - tvCardIdentity.setText("Not confirmed"); - tvCardIdentity.setTextColor(getResources().getColor(R.color.not_confirmed, getActivity().getTheme())); - } - - tvIssuer.setText(mCard.getIssuerDescription()); - tvIssuerData.setText(mCard.getIssuerDataDescription()); - - tvRegistrationDate.setText(mCard.getPersonalizationDateTimeDescription()); - - //tvBlockchain.setText(mCard.getBlockchain().getOfficialName()); - tvBlockchain.setText(mCard.getBlockchainName()); - ivBlockchain.setImageResource(mCard.getBlockchain().getImageResource(this.getContext(), mCard.getTokenSymbol())); - - if (mCard.isReusable()) { - tvReusable.setText("Reusable"); - } else { - tvReusable.setText("One-off banknote"); - } - - tvSigningMethod.setText(mCard.getSigningMethod().getDescription()); - - if (mCard.getStatus() == Tangem_Card.Status.Loaded || mCard.getStatus() == Tangem_Card.Status.Purged) { - - tvLastSigned.setText(mCard.getLastSignedDescription()); - if (mCard.getRemainingSignatures() == 0) { - tvRemainingSignatures.setTextColor(getResources().getColor(R.color.not_confirmed, getActivity().getTheme())); - tvRemainingSignatures.setText("None"); - } else if (mCard.getRemainingSignatures() == 1) { - tvRemainingSignatures.setTextColor(getResources().getColor(R.color.not_confirmed, getActivity().getTheme())); - tvRemainingSignatures.setText("Last one!"); - } else if (mCard.getRemainingSignatures() > 1000) { - tvRemainingSignatures.setTextColor(getResources().getColor(R.color.confirmed, getActivity().getTheme())); - tvRemainingSignatures.setText("Unlimited"); - } else { - tvRemainingSignatures.setTextColor(getResources().getColor(R.color.confirmed, getActivity().getTheme())); - tvRemainingSignatures.setText(String.valueOf(mCard.getRemainingSignatures())); - } - tvSignedTx.setText(String.valueOf(mCard.getMaxSignatures() - mCard.getRemainingSignatures())); - } else { - tvLastSigned.setText(""); - tvRemainingSignatures.setText(""); - tvSignedTx.setText(""); - } - - tvFirmware.setText(mCard.getFirmwareVersion()); - - String features = ""; - - if (mCard.allowSwapPIN() && mCard.allowSwapPIN2()) { - features += "Allows change PIN1 and PIN2\n"; - } else if (mCard.allowSwapPIN()) { - features += "Allows change PIN1\n"; - } else if (mCard.allowSwapPIN2()) { - features += "Allows change PIN2\n"; - } else { - features += "Fixed PIN1 and PIN2\n"; - } - - if (mCard.needCVC()) { - features += "Requires CVC\n"; - } - - if (mCard.supportDynamicNDEF()) { - features += "Dynamic NDEF for iOS\n"; - } else if (mCard.supportNDEF()) { - features += "NDEF\n"; - } - - if (mCard.supportBlock()) { - features += "Blockable\n"; - } - - if (mCard.supportOnlyOneCommandAtTime()) { - features += "Atomic command mode"; - } - - if (features.endsWith("\n")) { - features = features.substring(0, features.length() - 1); - } - tvFeatures.setText(features); - - if (mCard.useDefaultPIN1()) { - ivPIN.setImageResource(R.drawable.unlock_pin1); - ivPIN.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "This banknote is protected by default PIN1 code", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivPIN.setImageResource(R.drawable.lock_pin1); - ivPIN.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "This banknote is protected by user's PIN1 code", Toast.LENGTH_LONG).show(); - } - }); - } - - if (mCard.getPauseBeforePIN2() > 0 && (mCard.useDefaultPIN2() || !mCard.useSmartSecurityDelay())) { - ivPIN2orSecurityDelay.setImageResource(R.drawable.timer); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), String.format("This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code", mCard.getPauseBeforePIN2() / 1000.0), Toast.LENGTH_LONG).show(); - } - }); - - } else if (mCard.useDefaultPIN2()) { - ivPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "This banknote is protected by default PIN2 code", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2); - ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "This banknote is protected by user's PIN2 code", Toast.LENGTH_LONG).show(); - } - }); - } - - - if (mCard.useDevelopersFirmware()) { - ivDeveloperVersion.setImageResource(R.drawable.ic_developer_version); - ivDeveloperVersion.setVisibility(View.VISIBLE); - ivDeveloperVersion.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Toast.makeText(getContext(), "Unlocked banknote, only for development use", Toast.LENGTH_LONG).show(); - } - }); - } else { - ivDeveloperVersion.setVisibility(View.INVISIBLE); - } - - if (mCard.getStatus() == Tangem_Card.Status.Loaded) { - tvWallet.setText(mCard.getShortWalletString()); - if (mCard.isWalletPublicKeyValid()) { - tvWalletIdentity.setText("Possession proved"); - tvWalletIdentity.setTextColor(getResources().getColor(R.color.confirmed, getActivity().getTheme())); - } else { - tvWalletIdentity.setText("Possession NOT proved"); - tvWalletIdentity.setTextColor(getResources().getColor(R.color.not_confirmed, getActivity().getTheme())); - } - } else { - tvWallet.setText("not available"); - tvWalletIdentity.setText("-- -- --"); - } - - timerHideErrorAndMessage = new Timer(); - timerHideErrorAndMessage.schedule(new TimerTask() { - @Override - public void run() { - tvError.post(new Runnable() { - @Override - public void run() { - tvMessage.setVisibility(View.GONE); - tvError.setVisibility(View.GONE); - mCard.setError(null); - mCard.setMessage(null); - } - }); - } - }, 5000); - - - } catch (Exception e) { - e.printStackTrace(); - } - } - - Timer timerHideErrorAndMessage = null; - - public Intent prepareResultIntent() { - Intent data = new Intent(); - data.putExtra("UID", mCard.getUID()); - data.putExtra("Card", mCard.getAsBundle()); - return data; - } - - - @Override - public void onResume() { - super.onResume(); - mNfcManager.onResume(); - } - - @Override - public void onPause() { - super.onPause(); - mNfcManager.onPause(); - } - - @Override - public void onStop() { - super.onStop(); - mNfcManager.onStop(); - } - - @Override - public void onTagDiscovered(Tag tag) { - try { - Log.w(getClass().getName(), "Ignore discovered tag!"); - mNfcManager.IgnoreTag(tag); - } catch (IOException e) { - e.printStackTrace(); - } - } - -} +package com.tangem.wallet; + +import android.app.Activity; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.widget.SwipeRefreshLayout; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.tangem.cardReader.NfcManager; + +import java.io.IOException; +import java.util.Timer; +import java.util.TimerTask; + +public class VerifyCardActivityFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener, NfcAdapter.ReaderCallback { + + Tangem_Card mCard; + TextView tvCardID, tvManufacturer, tvRegistrationDate, tvCardIdentity, tvLastSigned, tvRemainingSignatures, tvReusable, tvOk, tvError, tvMessage, + tvIssuer, tvIssuerData, tvFeatures, tvBlockchain, tvSignedTx, tvSigningMethod, tvFirmware, tvWalletIdentity, tvWallet; + ImageView ivBlockchain, ivPIN, ivPIN2orSecurityDelay, ivDeveloperVersion; + SwipeRefreshLayout mSwipeRefreshLayout; + private NfcManager mNfcManager; + + public VerifyCardActivityFragment() { + + } + + public void onRefresh() { + mSwipeRefreshLayout.setRefreshing(false); + } + + @Override + public View onCreateView(final LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View v = inflater.inflate(R.layout.fragment_verify_card, container, false); + + mNfcManager = new NfcManager(this.getActivity(), this); + + + // SwipeRefreshLayout + mSwipeRefreshLayout = v.findViewById(R.id.swipe_container); + mSwipeRefreshLayout.setOnRefreshListener(this); + + mCard = new Tangem_Card(getActivity().getIntent().getStringExtra("UID")); + mCard.LoadFromBundle(getActivity().getIntent().getExtras().getBundle("Card")); + tvCardID = v.findViewById(R.id.tvCardID); + + tvLastSigned = v.findViewById(R.id.tvLastSigned); + tvRemainingSignatures = v.findViewById(R.id.tvRemainingSignatures); + + tvReusable = v.findViewById(R.id.tvReusable); + + tvManufacturer = v.findViewById(R.id.tvManufacturerInfo); + + tvCardIdentity = v.findViewById(R.id.tvCardIdentity); + + tvRegistrationDate = v.findViewById(R.id.tvCardRegistredDate); + + ivBlockchain = v.findViewById(R.id.imgBlockchain); + ivPIN = v.findViewById(R.id.imgPIN); + ivPIN2orSecurityDelay = v.findViewById(R.id.imgPIN2orSecurityDelay); + ivDeveloperVersion = v.findViewById(R.id.imgDeveloperVersion); + + tvError = v.findViewById(R.id.tvError); + tvMessage = v.findViewById(R.id.tvMessage); + + tvIssuer = v.findViewById(R.id.tvIssuer); + tvIssuerData = v.findViewById(R.id.tvIssuerData); + + tvFirmware = v.findViewById(R.id.tvFirmware); + tvFeatures = v.findViewById(R.id.tvFeatures); + tvBlockchain = v.findViewById(R.id.tvBlockchain); + + tvSignedTx = v.findViewById(R.id.tvSignedTx); + tvSigningMethod = v.findViewById(R.id.tvSigningMethod); + + tvOk = v.findViewById(R.id.tvOk); + if (tvOk != null) { + tvOk.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent data = prepareResultIntent(); + data.putExtra("modification", "update"); + getActivity().setResult(Activity.RESULT_OK, data); + getActivity().finish(); + } + }); + } + + tvWallet = v.findViewById(R.id.tvWallet); + tvWalletIdentity = v.findViewById(R.id.tvWalletIdentity); + + UpdateViews(); + +// if (NeedUpdate) { +// mSwipeRefreshLayout.setRefreshing(true); +// mSwipeRefreshLayout.postDelayed(new Runnable() { +// @Override +// public void run() { +// onRefresh(); +// } +// }, 1000); +// } + return v; + } + + void UpdateViews() { + try { + if (timerHideErrorAndMessage != null) { + timerHideErrorAndMessage.cancel(); + timerHideErrorAndMessage = null; + } + tvCardID.setText(mCard.getCIDDescription()); + + if (mCard.getError() == null || mCard.getError().isEmpty()) { + tvError.setVisibility(View.GONE); + tvError.setText(""); + } else { + tvError.setVisibility(View.VISIBLE); + tvError.setText(mCard.getError()); + } + if (mCard.getMessage() == null || mCard.getMessage().isEmpty()) { + tvMessage.setVisibility(View.GONE); + tvMessage.setText(""); + } else { + tvMessage.setVisibility(View.VISIBLE); + tvMessage.setText(mCard.getMessage()); + } + + tvManufacturer.setText(mCard.getManufacturer().getOfficialName()); + + if (mCard.isManufacturerConfirmed() && mCard.isCardPublicKeyValid()) { + tvCardIdentity.setText("Attested"); + tvCardIdentity.setTextColor(getResources().getColor(R.color.confirmed, getActivity().getTheme())); + } else { + tvCardIdentity.setText("Not confirmed"); + tvCardIdentity.setTextColor(getResources().getColor(R.color.not_confirmed, getActivity().getTheme())); + } + + tvIssuer.setText(mCard.getIssuerDescription()); + tvIssuerData.setText(mCard.getIssuerDataDescription()); + + tvRegistrationDate.setText(mCard.getPersonalizationDateTimeDescription()); + + //tvBlockchain.setText(mCard.getBlockchain().getOfficialName()); + tvBlockchain.setText(mCard.getBlockchainName()); + ivBlockchain.setImageResource(mCard.getBlockchain().getImageResource(this.getContext(), mCard.getTokenSymbol())); + + if (mCard.isReusable()) { + tvReusable.setText("Reusable"); + } else { + tvReusable.setText("One-off banknote"); + } + + tvSigningMethod.setText(mCard.getSigningMethod().getDescription()); + + if (mCard.getStatus() == Tangem_Card.Status.Loaded || mCard.getStatus() == Tangem_Card.Status.Purged) { + + tvLastSigned.setText(mCard.getLastSignedDescription()); + if (mCard.getRemainingSignatures() == 0) { + tvRemainingSignatures.setTextColor(getResources().getColor(R.color.not_confirmed, getActivity().getTheme())); + tvRemainingSignatures.setText("None"); + } else if (mCard.getRemainingSignatures() == 1) { + tvRemainingSignatures.setTextColor(getResources().getColor(R.color.not_confirmed, getActivity().getTheme())); + tvRemainingSignatures.setText("Last one!"); + } else if (mCard.getRemainingSignatures() > 1000) { + tvRemainingSignatures.setTextColor(getResources().getColor(R.color.confirmed, getActivity().getTheme())); + tvRemainingSignatures.setText("Unlimited"); + } else { + tvRemainingSignatures.setTextColor(getResources().getColor(R.color.confirmed, getActivity().getTheme())); + tvRemainingSignatures.setText(String.valueOf(mCard.getRemainingSignatures())); + } + tvSignedTx.setText(String.valueOf(mCard.getMaxSignatures() - mCard.getRemainingSignatures())); + } else { + tvLastSigned.setText(""); + tvRemainingSignatures.setText(""); + tvSignedTx.setText(""); + } + + tvFirmware.setText(mCard.getFirmwareVersion()); + + String features = ""; + + if (mCard.allowSwapPIN() && mCard.allowSwapPIN2()) { + features += "Allows change PIN1 and PIN2\n"; + } else if (mCard.allowSwapPIN()) { + features += "Allows change PIN1\n"; + } else if (mCard.allowSwapPIN2()) { + features += "Allows change PIN2\n"; + } else { + features += "Fixed PIN1 and PIN2\n"; + } + + if (mCard.needCVC()) { + features += "Requires CVC\n"; + } + + if (mCard.supportDynamicNDEF()) { + features += "Dynamic NDEF for iOS\n"; + } else if (mCard.supportNDEF()) { + features += "NDEF\n"; + } + + if (mCard.supportBlock()) { + features += "Blockable\n"; + } + + if (mCard.supportOnlyOneCommandAtTime()) { + features += "Atomic command mode"; + } + + if (features.endsWith("\n")) { + features = features.substring(0, features.length() - 1); + } + tvFeatures.setText(features); + + if (mCard.useDefaultPIN1()) { + ivPIN.setImageResource(R.drawable.unlock_pin1); + ivPIN.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "This banknote is protected by default PIN1 code", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivPIN.setImageResource(R.drawable.lock_pin1); + ivPIN.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "This banknote is protected by user's PIN1 code", Toast.LENGTH_LONG).show(); + } + }); + } + + if (mCard.getPauseBeforePIN2() > 0 && (mCard.useDefaultPIN2() || !mCard.useSmartSecurityDelay())) { + ivPIN2orSecurityDelay.setImageResource(R.drawable.timer); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), String.format("This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code", mCard.getPauseBeforePIN2() / 1000.0), Toast.LENGTH_LONG).show(); + } + }); + + } else if (mCard.useDefaultPIN2()) { + ivPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "This banknote is protected by default PIN2 code", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2); + ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "This banknote is protected by user's PIN2 code", Toast.LENGTH_LONG).show(); + } + }); + } + + + if (mCard.useDevelopersFirmware()) { + ivDeveloperVersion.setImageResource(R.drawable.ic_developer_version); + ivDeveloperVersion.setVisibility(View.VISIBLE); + ivDeveloperVersion.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Toast.makeText(getContext(), "Unlocked banknote, only for development use", Toast.LENGTH_LONG).show(); + } + }); + } else { + ivDeveloperVersion.setVisibility(View.INVISIBLE); + } + + if (mCard.getStatus() == Tangem_Card.Status.Loaded) { + tvWallet.setText(mCard.getShortWalletString()); + if (mCard.isWalletPublicKeyValid()) { + tvWalletIdentity.setText("Possession proved"); + tvWalletIdentity.setTextColor(getResources().getColor(R.color.confirmed, getActivity().getTheme())); + } else { + tvWalletIdentity.setText("Possession NOT proved"); + tvWalletIdentity.setTextColor(getResources().getColor(R.color.not_confirmed, getActivity().getTheme())); + } + } else { + tvWallet.setText("not available"); + tvWalletIdentity.setText("-- -- --"); + } + + timerHideErrorAndMessage = new Timer(); + timerHideErrorAndMessage.schedule(new TimerTask() { + @Override + public void run() { + tvError.post(new Runnable() { + @Override + public void run() { + tvMessage.setVisibility(View.GONE); + tvError.setVisibility(View.GONE); + mCard.setError(null); + mCard.setMessage(null); + } + }); + } + }, 5000); + + + } catch (Exception e) { + e.printStackTrace(); + } + } + + Timer timerHideErrorAndMessage = null; + + public Intent prepareResultIntent() { + Intent data = new Intent(); + data.putExtra("UID", mCard.getUID()); + data.putExtra("Card", mCard.getAsBundle()); + return data; + } + + + @Override + public void onResume() { + super.onResume(); + mNfcManager.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mNfcManager.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + mNfcManager.onStop(); + } + + @Override + public void onTagDiscovered(Tag tag) { + try { + Log.w(getClass().getName(), "Ignore discovered tag!"); + mNfcManager.IgnoreTag(tag); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/app/src/main/java/com/tangem/wallet/VerifyCardTask.java b/app/src/main/java/com/tangem/wallet/VerifyCardTask.java index bc0b7cf576..c18cce6c89 100644 --- a/app/src/main/java/com/tangem/wallet/VerifyCardTask.java +++ b/app/src/main/java/com/tangem/wallet/VerifyCardTask.java @@ -1,109 +1,109 @@ -package com.tangem.wallet; - -import android.content.Context; -import android.nfc.tech.IsoDep; -import android.util.Log; - -import com.tangem.cardReader.CardProtocol; -import com.tangem.cardReader.NfcManager; - -/** - * Created by dvol on 04.02.2018. - */ - -public class VerifyCardTask extends Thread { - - IsoDep mIsoDep; - CardProtocol.Notifications mNotifications; - private final String logTag = "VerifyCardTask"; - private boolean isCancelled = false; - private Context mContext; - private Tangem_Card mCard; - private NfcManager mNfcManager; - - VerifyCardTask(Context context, Tangem_Card card, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) { - mCard = card; - mContext = context; - mIsoDep = isoDep; - mNotifications = notifications; - mNfcManager = nfcManager; - } - - @Override - public void run() { - if (mIsoDep == null) { - return; - } - try { - // for Samsung's bugs - - // Workaround for the Samsung Galaxy S5 (since the - // first connection always hangs on transceive). - int timeout = mIsoDep.getTimeout(); - mIsoDep.connect(); - mIsoDep.close(); - mIsoDep.connect(); - mIsoDep.setTimeout(timeout); - try { - CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications); - mNotifications.OnReadStart(protocol); - try { - mNotifications.OnReadProgress(protocol, 5); - - Log.i("VerifyCardTask", "[-- Start verify card --]"); - - if (isCancelled) return; - - String PIN = mCard.getPIN(); - protocol.setPIN(PIN); - protocol.run_Read(); - PINStorage.setLastUsedPIN(PIN); - mNotifications.OnReadProgress(protocol, 30); - if (isCancelled) return; - protocol.run_VerifyCard(); - mNotifications.OnReadProgress(protocol, 60); - Log.i("VerifyCardTask", "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName()); - if (isCancelled) return; - if (protocol.getCard().getStatus() == Tangem_Card.Status.Loaded) { - protocol.run_CheckWalletWithSignatureVerify(); - mNotifications.OnReadProgress(protocol, 90); - } - - - -// if (isCancelled) return; -// if (protocol.getCard().getStatus() == Tangem_Card.Status.Loaded) { -// protocol.run_CheckWithSignatureVerify(); -// } - - } catch (Exception e) { - e.printStackTrace(); - protocol.setError(e); - - } finally { - Log.i("VerifyCardTask", "[-- Finish verify card --]"); - mNotifications.OnReadFinish(protocol); - } - } finally { - mNfcManager.IgnoreTag(mIsoDep.getTag()); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - void cancel(Boolean AllowInterrupt) { - try { - if (this.isAlive()) { - isCancelled = true; - join(500); - } - if (this.isAlive() && AllowInterrupt) { - interrupt(); - mNotifications.OnReadCancel(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - -} +package com.tangem.wallet; + +import android.content.Context; +import android.nfc.tech.IsoDep; +import android.util.Log; + +import com.tangem.cardReader.CardProtocol; +import com.tangem.cardReader.NfcManager; + +/** + * Created by dvol on 04.02.2018. + */ + +public class VerifyCardTask extends Thread { + + IsoDep mIsoDep; + CardProtocol.Notifications mNotifications; + private final String logTag = "VerifyCardTask"; + private boolean isCancelled = false; + private Context mContext; + private Tangem_Card mCard; + private NfcManager mNfcManager; + + VerifyCardTask(Context context, Tangem_Card card, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications) { + mCard = card; + mContext = context; + mIsoDep = isoDep; + mNotifications = notifications; + mNfcManager = nfcManager; + } + + @Override + public void run() { + if (mIsoDep == null) { + return; + } + try { + // for Samsung's bugs - + // Workaround for the Samsung Galaxy S5 (since the + // first connection always hangs on transceive). + int timeout = mIsoDep.getTimeout(); + mIsoDep.connect(); + mIsoDep.close(); + mIsoDep.connect(); + mIsoDep.setTimeout(timeout); + try { + CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCard, mNotifications); + mNotifications.OnReadStart(protocol); + try { + mNotifications.OnReadProgress(protocol, 5); + + Log.i("VerifyCardTask", "[-- Start verify card --]"); + + if (isCancelled) return; + + String PIN = mCard.getPIN(); + protocol.setPIN(PIN); + protocol.run_Read(); + PINStorage.setLastUsedPIN(PIN); + mNotifications.OnReadProgress(protocol, 30); + if (isCancelled) return; + protocol.run_VerifyCard(); + mNotifications.OnReadProgress(protocol, 60); + Log.i("VerifyCardTask", "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName()); + if (isCancelled) return; + if (protocol.getCard().getStatus() == Tangem_Card.Status.Loaded) { + protocol.run_CheckWalletWithSignatureVerify(); + mNotifications.OnReadProgress(protocol, 90); + } + + + +// if (isCancelled) return; +// if (protocol.getCard().getStatus() == Tangem_Card.Status.Loaded) { +// protocol.run_CheckWithSignatureVerify(); +// } + + } catch (Exception e) { + e.printStackTrace(); + protocol.setError(e); + + } finally { + Log.i("VerifyCardTask", "[-- Finish verify card --]"); + mNotifications.OnReadFinish(protocol); + } + } finally { + mNfcManager.IgnoreTag(mIsoDep.getTag()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + void cancel(Boolean AllowInterrupt) { + try { + if (this.isAlive()) { + isCancelled = true; + join(500); + } + if (this.isAlive() && AllowInterrupt) { + interrupt(); + mNotifications.OnReadCancel(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/app/src/main/java/com/tangem/wallet/VerticalTextView.java b/app/src/main/java/com/tangem/wallet/VerticalTextView.java index 53bbecdb9a..d63b26cdb2 100644 --- a/app/src/main/java/com/tangem/wallet/VerticalTextView.java +++ b/app/src/main/java/com/tangem/wallet/VerticalTextView.java @@ -1,70 +1,70 @@ -package com.tangem.wallet; - -import android.content.Context; -import android.graphics.Canvas; -import android.text.TextPaint; -import android.util.AttributeSet; -import android.view.Gravity; -import android.widget.TextView; - -public class VerticalTextView extends TextView -{ - final boolean topDown; - - public VerticalTextView( Context context, - AttributeSet attrs ) - { - super( context, attrs ); - final int gravity = getGravity(); - if ( Gravity.isVertical( gravity ) - && ( gravity & Gravity.VERTICAL_GRAVITY_MASK ) - == Gravity.BOTTOM ) - { - setGravity( - ( gravity & Gravity.HORIZONTAL_GRAVITY_MASK ) - | Gravity.TOP ); - topDown = false; - } - else - { - topDown = true; - } - } - - @Override - protected void onMeasure( int widthMeasureSpec, - int heightMeasureSpec ) - { - super.onMeasure( heightMeasureSpec, - widthMeasureSpec ); - setMeasuredDimension( getMeasuredHeight(), - getMeasuredWidth() ); - } - - @Override - protected void onDraw( Canvas canvas ) - { - TextPaint textPaint = getPaint(); - textPaint.setColor( getCurrentTextColor() ); - textPaint.drawableState = getDrawableState(); - - canvas.save(); - - if ( topDown ) - { - canvas.translate( getWidth(), 0 ); - canvas.rotate( 90 ); - } - else - { - canvas.translate( 0, getHeight() ); - canvas.rotate( -90 ); - } - - canvas.translate( getCompoundPaddingLeft(), - getExtendedPaddingTop() ); - - getLayout().draw( canvas ); - canvas.restore(); - } +package com.tangem.wallet; + +import android.content.Context; +import android.graphics.Canvas; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.view.Gravity; +import android.widget.TextView; + +public class VerticalTextView extends TextView +{ + final boolean topDown; + + public VerticalTextView( Context context, + AttributeSet attrs ) + { + super( context, attrs ); + final int gravity = getGravity(); + if ( Gravity.isVertical( gravity ) + && ( gravity & Gravity.VERTICAL_GRAVITY_MASK ) + == Gravity.BOTTOM ) + { + setGravity( + ( gravity & Gravity.HORIZONTAL_GRAVITY_MASK ) + | Gravity.TOP ); + topDown = false; + } + else + { + topDown = true; + } + } + + @Override + protected void onMeasure( int widthMeasureSpec, + int heightMeasureSpec ) + { + super.onMeasure( heightMeasureSpec, + widthMeasureSpec ); + setMeasuredDimension( getMeasuredHeight(), + getMeasuredWidth() ); + } + + @Override + protected void onDraw( Canvas canvas ) + { + TextPaint textPaint = getPaint(); + textPaint.setColor( getCurrentTextColor() ); + textPaint.drawableState = getDrawableState(); + + canvas.save(); + + if ( topDown ) + { + canvas.translate( getWidth(), 0 ); + canvas.rotate( 90 ); + } + else + { + canvas.translate( 0, getHeight() ); + canvas.rotate( -90 ); + } + + canvas.translate( getCompoundPaddingLeft(), + getExtendedPaddingTop() ); + + getLayout().draw( canvas ); + canvas.restore(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/WaitSecurityDelayDialog.java b/app/src/main/java/com/tangem/wallet/WaitSecurityDelayDialog.java index df1c2ac71e..e22fa55b07 100644 --- a/app/src/main/java/com/tangem/wallet/WaitSecurityDelayDialog.java +++ b/app/src/main/java/com/tangem/wallet/WaitSecurityDelayDialog.java @@ -1,166 +1,166 @@ -package com.tangem.wallet; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ProgressBar; - -import java.util.Timer; -import java.util.TimerTask; - -/** - * Created by dvol on 06.03.2018. - */ -public class WaitSecurityDelayDialog extends DialogFragment { - ProgressBar progressBar; - int msTimeout = 60000, msProgress = 0; - Timer timer; - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - LayoutInflater inflater = getActivity().getLayoutInflater(); - - // Inflate and set the layout for the dialog - // Pass null as the parent view because its going in the dialog layout - View v = inflater.inflate(R.layout.dialog_wait_pin2, null); - - progressBar = v.findViewById(R.id.progressBar); - progressBar.setMax(msTimeout); - progressBar.setProgress(msProgress); - - timer = new Timer(); - timer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - progressBar.post(new Runnable() { - @Override - public void run() { - int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); - if (progress < WaitSecurityDelayDialog.this.progressBar.getMax()) { - WaitSecurityDelayDialog.this.progressBar.setProgress(progress + 1000); - } - } - }); - } - }, 1000, 1000); - return new AlertDialog.Builder(getActivity()) - .setIcon(R.drawable.tangem_logo_small_new) - .setTitle("Security delay") - .setView(v) - .setCancelable(false) - .create(); - } - - @Override - public void onCancel(DialogInterface dialog) { - super.onCancel(dialog); - } - - public void setup(int msTimeout, int msProgress) { - this.msTimeout = msTimeout; - this.msProgress = msProgress; - } - - public void setRemainingTimeout(final int msec) { - progressBar.post(new Runnable() { - @Override - public void run() { - int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); - if (timer != null) { - // we get delay latency from card for first time - don't change progress by timer, only by card answer - progressBar.setMax(progress + msec); - timer.cancel(); - timer = null; - } else { - int newProgress = progressBar.getMax() - msec; - if (newProgress > progress) { - progressBar.setProgress(newProgress); - } else { - progressBar.setMax(progress + msec); - } - } - } - }); - } - - static Timer timerToShowDelayDialog = null; - static WaitSecurityDelayDialog instance = null; - - public static WaitSecurityDelayDialog getInstance() { - if (instance == null) { - instance = new WaitSecurityDelayDialog(); - } - return instance; - } - - private final static int MinRemainingDelayToShowDialog=1000; - private final static int DelayBeforeShowDialog=5000; - - public static void onReadBeforeRequest(final Activity activity, final int timeout) { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - if (timerToShowDelayDialog != null || timeout < DelayBeforeShowDialog+MinRemainingDelayToShowDialog) return; - timerToShowDelayDialog = new Timer(); - timerToShowDelayDialog.schedule(new TimerTask() { - @Override - public void run() { - if (WaitSecurityDelayDialog.instance != null) return; - instance = new WaitSecurityDelayDialog(); - instance.setup(timeout, DelayBeforeShowDialog); - instance.setCancelable(false); - instance.show(activity.getFragmentManager(), "WaitSecurityDelayDialog"); - } - }, DelayBeforeShowDialog); - } - }); - } - - public static void onReadAfterRequest(final Activity activity) { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - if (timerToShowDelayDialog == null) return; - timerToShowDelayDialog.cancel(); - timerToShowDelayDialog = null; - } - }); - } - - public static void OnReadWait(final Activity activity, final int msec) { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - if (timerToShowDelayDialog != null) { - timerToShowDelayDialog.cancel(); - timerToShowDelayDialog = null; - } - - if (msec == 0) { - if (instance != null) { - instance.dismiss(); - instance = null; - } - return; - } - if (instance == null) { - if( msec>MinRemainingDelayToShowDialog ) { - instance = new WaitSecurityDelayDialog(); - // 1000ms - card delay notification interval - instance.setup(msec + 1000, 1000); - instance.setCancelable(false); - instance.show(activity.getFragmentManager(), "WaitSecurityDelayDialog"); - } - } else { - instance.setRemainingTimeout(msec); - } - } - }); - } -} +package com.tangem.wallet; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ProgressBar; + +import java.util.Timer; +import java.util.TimerTask; + +/** + * Created by dvol on 06.03.2018. + */ +public class WaitSecurityDelayDialog extends DialogFragment { + ProgressBar progressBar; + int msTimeout = 60000, msProgress = 0; + Timer timer; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + LayoutInflater inflater = getActivity().getLayoutInflater(); + + // Inflate and set the layout for the dialog + // Pass null as the parent view because its going in the dialog layout + View v = inflater.inflate(R.layout.dialog_wait_pin2, null); + + progressBar = v.findViewById(R.id.progressBar); + progressBar.setMax(msTimeout); + progressBar.setProgress(msProgress); + + timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + progressBar.post(new Runnable() { + @Override + public void run() { + int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); + if (progress < WaitSecurityDelayDialog.this.progressBar.getMax()) { + WaitSecurityDelayDialog.this.progressBar.setProgress(progress + 1000); + } + } + }); + } + }, 1000, 1000); + return new AlertDialog.Builder(getActivity()) + .setIcon(R.drawable.tangem_logo_small_new) + .setTitle("Security delay") + .setView(v) + .setCancelable(false) + .create(); + } + + @Override + public void onCancel(DialogInterface dialog) { + super.onCancel(dialog); + } + + public void setup(int msTimeout, int msProgress) { + this.msTimeout = msTimeout; + this.msProgress = msProgress; + } + + public void setRemainingTimeout(final int msec) { + progressBar.post(new Runnable() { + @Override + public void run() { + int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); + if (timer != null) { + // we get delay latency from card for first time - don't change progress by timer, only by card answer + progressBar.setMax(progress + msec); + timer.cancel(); + timer = null; + } else { + int newProgress = progressBar.getMax() - msec; + if (newProgress > progress) { + progressBar.setProgress(newProgress); + } else { + progressBar.setMax(progress + msec); + } + } + } + }); + } + + static Timer timerToShowDelayDialog = null; + static WaitSecurityDelayDialog instance = null; + + public static WaitSecurityDelayDialog getInstance() { + if (instance == null) { + instance = new WaitSecurityDelayDialog(); + } + return instance; + } + + private final static int MinRemainingDelayToShowDialog=1000; + private final static int DelayBeforeShowDialog=5000; + + public static void onReadBeforeRequest(final Activity activity, final int timeout) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (timerToShowDelayDialog != null || timeout < DelayBeforeShowDialog+MinRemainingDelayToShowDialog) return; + timerToShowDelayDialog = new Timer(); + timerToShowDelayDialog.schedule(new TimerTask() { + @Override + public void run() { + if (WaitSecurityDelayDialog.instance != null) return; + instance = new WaitSecurityDelayDialog(); + instance.setup(timeout, DelayBeforeShowDialog); + instance.setCancelable(false); + instance.show(activity.getFragmentManager(), "WaitSecurityDelayDialog"); + } + }, DelayBeforeShowDialog); + } + }); + } + + public static void onReadAfterRequest(final Activity activity) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (timerToShowDelayDialog == null) return; + timerToShowDelayDialog.cancel(); + timerToShowDelayDialog = null; + } + }); + } + + public static void OnReadWait(final Activity activity, final int msec) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (timerToShowDelayDialog != null) { + timerToShowDelayDialog.cancel(); + timerToShowDelayDialog = null; + } + + if (msec == 0) { + if (instance != null) { + instance.dismiss(); + instance = null; + } + return; + } + if (instance == null) { + if( msec>MinRemainingDelayToShowDialog ) { + instance = new WaitSecurityDelayDialog(); + // 1000ms - card delay notification interval + instance.setup(msec + 1000, 1000); + instance.setCancelable(false); + instance.show(activity.getFragmentManager(), "WaitSecurityDelayDialog"); + } + } else { + instance.setRemainingTimeout(msec); + } + } + }); + } +} diff --git a/app/src/main/java/com/tangem/wallet/WalletInfoFragment.java b/app/src/main/java/com/tangem/wallet/WalletInfoFragment.java index 924429f3e5..183b5c9171 100644 --- a/app/src/main/java/com/tangem/wallet/WalletInfoFragment.java +++ b/app/src/main/java/com/tangem/wallet/WalletInfoFragment.java @@ -1,150 +1,150 @@ -package com.tangem.wallet; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.EncodeHintType; -import com.google.zxing.WriterException; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.qrcode.QRCodeWriter; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; - -import java.util.Hashtable; - -import static android.content.Context.CLIPBOARD_SERVICE; - -/** - * A simple {@link Fragment} subclass. - * Activities that contain this fragment must implement the - * {@link OnFragmentInteractionListener} interface - * to handle interaction events. - * Use the {@link WalletInfoFragment#newInstance} factory method to - * create an instance of this fragment. - */ -public class WalletInfoFragment extends Fragment { - - // TODO: Rename and change types of parameters - private Tangem_Card mCard; - - private OnFragmentInteractionListener mListener; - - public WalletInfoFragment() { - // Required empty public constructor - } - - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @return A new instance of fragment WalletInfoFragment. - */ - // TODO: Rename and change types and number of parameters - public static WalletInfoFragment newInstance(Tangem_Card card) { - WalletInfoFragment fragment = new WalletInfoFragment(); - Bundle args = new Bundle(); - args.putString("UID",card.getUID()); - card.SaveToBundle(args); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - mCard = new Tangem_Card(getArguments().getString("UID")); - mCard.LoadFromBundle(getArguments()); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - View result=inflater.inflate(R.layout.fragment_wallet_info, container, false); - - ImageView mImage= (ImageView)result.findViewById(R.id.qrWallet); - try { - mImage.setImageBitmap(generateQrCode(mCard.getWallet())); - } catch (WriterException e) { - e.printStackTrace(); - } - TextView mText=(TextView)result.findViewById(R.id.strWallet); - mText.setText(mCard.getWallet()); - - mText.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - TextView mText = (TextView) view; - ClipboardManager clipboard = (ClipboardManager)getActivity().getSystemService(CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(ClipData.newPlainText(mText.getText(), mText.getText())); - Toast.makeText(getContext(),"Copied to clipboard",Toast.LENGTH_LONG).show(); - } - }); - return result; - } - - public static Bitmap generateQrCode(String myCodeText) throws WriterException { - Hashtable hintMap = new Hashtable(); - hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // H = 30% damage - - QRCodeWriter qrCodeWriter = new QRCodeWriter(); - - int size = 256; - - BitMatrix bitMatrix = qrCodeWriter.encode(myCodeText, BarcodeFormat.QR_CODE, size, size, hintMap); - int width = bitMatrix.getWidth(); - Bitmap bmp = Bitmap.createBitmap(width, width, Bitmap.Config.RGB_565); - for (int x = 0; x < width; x++) { - for (int y = 0; y < width; y++) { - bmp.setPixel(y, x, bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE); - } - } - return bmp; - } - - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof OnFragmentInteractionListener) { - mListener = (OnFragmentInteractionListener) context; - } else { - throw new RuntimeException(context.toString() - + " must implement OnFragmentInteractionListener"); - } - } - - @Override - public void onDetach() { - super.onDetach(); - mListener = null; - } - - /** - * This interface must be implemented by activities that contain this - * fragment to allow an interaction in this fragment to be communicated - * to the activity and potentially other fragments contained in that - * activity. - *

- * See the Android Training lesson Communicating with Other Fragments for more information. - */ - public interface OnFragmentInteractionListener { - // TODO: Update argument type and name -// void onFragmentInteraction(Uri uri); - } -} +package com.tangem.wallet; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; + +import java.util.Hashtable; + +import static android.content.Context.CLIPBOARD_SERVICE; + +/** + * A simple {@link Fragment} subclass. + * Activities that contain this fragment must implement the + * {@link OnFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link WalletInfoFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class WalletInfoFragment extends Fragment { + + // TODO: Rename and change types of parameters + private Tangem_Card mCard; + + private OnFragmentInteractionListener mListener; + + public WalletInfoFragment() { + // Required empty public constructor + } + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @return A new instance of fragment WalletInfoFragment. + */ + // TODO: Rename and change types and number of parameters + public static WalletInfoFragment newInstance(Tangem_Card card) { + WalletInfoFragment fragment = new WalletInfoFragment(); + Bundle args = new Bundle(); + args.putString("UID",card.getUID()); + card.SaveToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mCard = new Tangem_Card(getArguments().getString("UID")); + mCard.LoadFromBundle(getArguments()); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + View result=inflater.inflate(R.layout.fragment_wallet_info, container, false); + + ImageView mImage= (ImageView)result.findViewById(R.id.qrWallet); + try { + mImage.setImageBitmap(generateQrCode(mCard.getWallet())); + } catch (WriterException e) { + e.printStackTrace(); + } + TextView mText=(TextView)result.findViewById(R.id.strWallet); + mText.setText(mCard.getWallet()); + + mText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + TextView mText = (TextView) view; + ClipboardManager clipboard = (ClipboardManager)getActivity().getSystemService(CLIPBOARD_SERVICE); + clipboard.setPrimaryClip(ClipData.newPlainText(mText.getText(), mText.getText())); + Toast.makeText(getContext(),"Copied to clipboard",Toast.LENGTH_LONG).show(); + } + }); + return result; + } + + public static Bitmap generateQrCode(String myCodeText) throws WriterException { + Hashtable hintMap = new Hashtable(); + hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // H = 30% damage + + QRCodeWriter qrCodeWriter = new QRCodeWriter(); + + int size = 256; + + BitMatrix bitMatrix = qrCodeWriter.encode(myCodeText, BarcodeFormat.QR_CODE, size, size, hintMap); + int width = bitMatrix.getWidth(); + Bitmap bmp = Bitmap.createBitmap(width, width, Bitmap.Config.RGB_565); + for (int x = 0; x < width; x++) { + for (int y = 0; y < width; y++) { + bmp.setPixel(y, x, bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE); + } + } + return bmp; + } + + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof OnFragmentInteractionListener) { + mListener = (OnFragmentInteractionListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnFragmentInteractionListener"); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mListener = null; + } + + /** + * This interface must be implemented by activities that contain this + * fragment to allow an interaction in this fragment to be communicated + * to the activity and potentially other fragments contained in that + * activity. + *

+ * See the Android Training lesson Communicating with Other Fragments for more information. + */ + public interface OnFragmentInteractionListener { + // TODO: Update argument type and name +// void onFragmentInteraction(Uri uri); + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml index c7bd21dbd8..ddb26ad776 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -1,34 +1,34 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/app/src/main/res/drawable/border.xml b/app/src/main/res/drawable/border.xml index 1707c3802a..86a1dd6daa 100644 --- a/app/src/main/res/drawable/border.xml +++ b/app/src/main/res/drawable/border.xml @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index d5fccc538c..3a37cf6d00 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,170 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/parsed_msg_bg_pressed.xml b/app/src/main/res/drawable/parsed_msg_bg_pressed.xml index 47b21d4368..ee42ad99aa 100644 --- a/app/src/main/res/drawable/parsed_msg_bg_pressed.xml +++ b/app/src/main/res/drawable/parsed_msg_bg_pressed.xml @@ -1,6 +1,6 @@ - - - - - + + + + + diff --git a/app/src/main/res/drawable/parsed_msg_bg_states.xml b/app/src/main/res/drawable/parsed_msg_bg_states.xml index 91aff98446..42b2bd1759 100644 --- a/app/src/main/res/drawable/parsed_msg_bg_states.xml +++ b/app/src/main/res/drawable/parsed_msg_bg_states.xml @@ -1,8 +1,8 @@ - - - - - - + + + + + + diff --git a/app/src/main/res/layout/activity_card_info.xml b/app/src/main/res/layout/activity_card_info.xml index fdf12aedb8..436175cfff 100644 --- a/app/src/main/res/layout/activity_card_info.xml +++ b/app/src/main/res/layout/activity_card_info.xml @@ -1,49 +1,49 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_confirm_payment.xml b/app/src/main/res/layout/activity_confirm_payment.xml index b32b90dd10..703e672425 100644 --- a/app/src/main/res/layout/activity_confirm_payment.xml +++ b/app/src/main/res/layout/activity_confirm_payment.xml @@ -1,423 +1,423 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -