Updated on 2026-08-14

This commit is contained in:
Tangem 2019-06-13 17:23:45 +03:00
commit bd8d84a85a
680 changed files with 51751 additions and 6799 deletions

View file

@ -1,107 +0,0 @@
package com.tangem;
import android.app.Application;
import android.support.v7.app.AppCompatDelegate;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.tangem.tangemserver.android.data.LocalStorage;
import com.tangem.di.DaggerNavigatorComponent;
import com.tangem.di.DaggerNetworkComponent;
import com.tangem.di.NavigatorComponent;
import com.tangem.di.NetworkComponent;
import com.tangem.tangemcard.data.Issuer;
import com.tangem.tangemcard.android.data.Firmwares;
import com.tangem.tangemcard.android.data.PINStorage;
import com.tangem.tangemcard.data.external.FirmwaresDigestsProvider;
import com.tangem.tangemcard.data.external.PINsProvider;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class App extends Application {
/**
* A singleton instance of the application class for easy access in other places
*/
private static App sInstance;
public App() {
super();
}
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
private static NetworkComponent networkComponent;
private static NavigatorComponent navigatorComponent;
public static NavigatorComponent getNavigatorComponent() {
return navigatorComponent;
}
public static LocalStorage localStorage;
public static PINsProvider pinStorage;
public static FirmwaresDigestsProvider firmwaresStorage;
@Override
public void onCreate() {
super.onCreate();
// initialize the singleton
sInstance = this;
networkComponent = DaggerNetworkComponent.create();
navigatorComponent = buildNavigatorComponent();
// common init
if (PINStorage.needInit())
PINStorage.init(getApplicationContext());
initIssuers();
firmwaresStorage = new Firmwares(getApplicationContext());
localStorage = new LocalStorage(getApplicationContext());
pinStorage = new PINStorage();
}
/**
* @return singleton instance
*/
public static synchronized App getInstance() {
return sInstance;
}
public static NetworkComponent getNetworkComponent() {
return networkComponent;
}
protected NavigatorComponent buildNavigatorComponent() {
return DaggerNavigatorComponent.builder()
.build();
}
public void initIssuers() {
try {
try (InputStream is = getApplicationContext().getAssets().open("issuers.json")) {
try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
Type listType = new TypeToken<List<Issuer>>() {
}.getType();
Issuer.fillIssuers(new Gson().fromJson(reader, listType));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,106 @@
package com.tangem
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tangem.data.local.PendingTransactionsStorage
import com.tangem.card_android.android.data.Firmwares
import com.tangem.card_android.android.data.PINStorage
import com.tangem.card_common.data.Issuer
import com.tangem.data.dp.PrefsManager
import com.tangem.di.*
import com.tangem.server_android.data.LocalStorage
import com.tangem.wallet.BuildConfig
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
class App : Application() {
companion object {
@get:Synchronized
var instance: App? = null
private set
init {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
}
lateinit var networkComponent: NetworkComponent
lateinit var navigatorComponent: NavigatorComponent
lateinit var toastHelperComponent: ToastHelperComponent
lateinit var firmwaresStorage: Firmwares
lateinit var localStorage: LocalStorage
lateinit var pinStorage: PINStorage
lateinit var pendingTransactionsStorage: PendingTransactionsStorage
}
override fun onCreate() {
super.onCreate()
// initialize the singleton
instance = this
networkComponent = DaggerNetworkComponent.create()
navigatorComponent = buildNavigatorComponent()
toastHelperComponent = buildToastHelperComponent()
PrefsManager.getInstance().init(this)
// common init
if (PINStorage.needInit())
PINStorage.init(applicationContext)
initIssuers()
firmwaresStorage = Firmwares(applicationContext)
localStorage = LocalStorage(applicationContext)
pinStorage = PINStorage()
pendingTransactionsStorage = PendingTransactionsStorage(applicationContext)
if (BuildConfig.DEBUG) {
com.tangem.card_common.util.Log.setLogger(
object : com.tangem.card_common.util.LoggerInterface {
override fun i(logTag: String, message: String) {
android.util.Log.i(logTag, message)
}
override fun e(logTag: String, message: String) {
android.util.Log.e(logTag, message)
}
override fun v(logTag: String, message: String) {
android.util.Log.v(logTag, message)
}
}
)
}
}
private fun buildNavigatorComponent(): NavigatorComponent {
return DaggerNavigatorComponent.builder()
.build()
}
private fun buildToastHelperComponent(): ToastHelperComponent {
return DaggerToastHelperComponent.builder()
.build()
}
private fun initIssuers() {
try {
applicationContext.assets.open("issuers.json").use { `is` ->
InputStreamReader(`is`, StandardCharsets.UTF_8).use { reader ->
val listType = object : TypeToken<List<Issuer>>() {
}.type
Issuer.fillIssuers(Gson().fromJson(reader, listType))
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View file

@ -4,16 +4,30 @@ import android.app.Activity
object Constant {
const val FLAVOR_TANGEM_ACCESS = "tangemAccess"
const val FLAVOR_TANGEM_CARDANO = "tangemCardano"
const val PREF_LAST_WALLET_ADDRESS = "last_wallet_address"
const val URL_TANGEM = "https://play.google.com/store/apps/details?id=com.tangem.wallet"
const val EXTRA_BLOCKCHAIN_DATA = "BLOCKCHAIN_DATA"
const val WALLET_ADDRESS = "Wallet address"
const val EXTRA_MESSAGE = "message"
const val EXTRA_MODIFICATION = "modification"
const val EXTRA_MODIFICATION_DELETE = "delete"
const val EXTRA_MODIFICATION_UPDATE = "update"
// LoadedWallet, VerifyCard
const val REQUEST_CODE_SEND_PAYMENT = 1
const val EXTRA_MODE = "mode"
const val INTENT_TYPE_TEXT_PLAIN = "text/plain"
// LoadedWalletFragment, VerifyCard
const val REQUEST_CODE_SEND_TRANSACTION = 1
const val REQUEST_CODE_PURGE = 2
const val REQUEST_CODE_REQUEST_PIN2_FOR_PURGE = 3
const val REQUEST_CODE_VERIFY_CARD = 4
@ -21,7 +35,7 @@ object Constant {
const val REQUEST_CODE_ENTER_NEW_PIN2 = 6
const val REQUEST_CODE_REQUEST_PIN2_FOR_SWAP_PIN = 7
const val REQUEST_CODE_SWAP_PIN = 8
const val REQUEST_CODE_RECEIVE_PAYMENT = 9
const val REQUEST_CODE_RECEIVE_TRANSACTION = 9
// MainActivity
const val REQUEST_CODE_SHOW_CARD_ACTIVITY = 1
@ -37,7 +51,7 @@ object Constant {
const val MILLIS_AUTO_HIDE = 1000
// PinRequestActivity
const val EXTRA_MODE = "mode"
const val KEY_ALIAS = "pinKey"
const val KEYSTORE = "AndroidKeyStore"
@ -57,26 +71,26 @@ object Constant {
const val REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY = 2
const val REQUEST_CODE_REQUEST_PIN2 = 3
// ConfirmPaymentActivity
const val REQUEST_CODE_SIGN_PAYMENT = 1
// ConfirmTransactionActivity
const val REQUEST_CODE_SIGN_TRANSACTION = 1
const val REQUEST_CODE_REQUEST_PIN2_ = 2
// SendTransactionActivity
const val EXTRA_TX: String = "TX"
// SignPaymentActivity
// SignTransactionActivity
const val EXTRA_AMOUNT = "Amount"
const val EXTRA_AMOUNT_CURRENCY = "AmountCurrency"
const val EXTRA_FEE = "Fee"
const val EXTRA_FEE_CURRENCY = "FeeCurrency"
const val EXTRA_FEE_INCLUDED = "FeeIncluded"
const val EXTRA_TARGET_ADDRESS = "TargetAddress"
const val REQUEST_CODE_SEND_PAYMENT_ = 1
const val REQUEST_CODE_SEND_TRANSACTION_ = 1
const val RESULT_INVALID_PIN_ = Activity.RESULT_FIRST_USER
// PreparePaymentActivity
// PrepareTransactionActivity
const val REQUEST_CODE_SCAN_QR = 1
const val REQUEST_CODE_SEND_PAYMENT__ = 2
const val REQUEST_CODE_SEND_TRANSACTION__ = 2
// PrepareCryptonitOtherApiWithdrawalActivity
const val REQUEST_CODE_SCAN_QR_KEY = 1

View file

@ -1,29 +1,41 @@
package com.tangem.data;
import com.tangem.tangemcard.R;
import com.tangem.card_android.R;
/**
* Created by dvol on 06.08.2017.
*/
public enum Blockchain {
Unknown("", "", R.drawable.ic_logo_unknown, ""),
Bitcoin("BTC", "BTC", R.drawable.ic_logo_bitcoin, "Bitcoin"),
BitcoinTestNet("BTC/test", "BTC", R.drawable.ic_logo_bitcoin_testnet, "Bitcoin Testnet"),
Ethereum("ETH", "ETH", R.drawable.ic_logo_ethereum, "Ethereum"),
EthereumTestNet("ETH/test", "ETH", R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"),
Token("Token", "ERC20", R.drawable.ic_logo_bat_token, "Ethereum"),
BitcoinCash("BCH", "BCH", R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash"),
Litecoin("LTC", "LTC", R.drawable.ic_logo_bitcoin, "Litecoin"),
Stellar("XLM", "XLM", R.drawable.ic_logo_stellar, "Stellar Lumens"),
StellarTestNet("XLM/test", "XLM", R.drawable.ic_logo_stellar, "Stellar Lumens");
Blockchain(String ID, String currency, int imageResource, String officialName) {
Unknown("", "", 1.0, R.drawable.ic_logo_unknown, ""),
Bitcoin("BTC", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"),
BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin_testnet, "Bitcoin Testnet"),
Ethereum("ETH", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"),
Token("Token", "ETH", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"),
NftToken("NftToken", "", 1.0, R.drawable.tangem2, "Ethereum"),
BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash"),
Litecoin("LTC", "LTC", 100000000.0, R.drawable.tangem2, "Litecoin"),
Rootstock("RSK", "RBTC", 1.0, R.drawable.tangem2, "RSK"),
RootstockToken("RskToken", "RBTC", 1.0, R.drawable.tangem2, "RSK"),
Cardano("CARDANO", "ADA", 1000000.0, R.drawable.tangem2, "Cardano"),
Ripple ("XRP", "XRP", 1000000.0, R.drawable.tangem2, "XRP"),
Binance("BINANCE", "BNB", 100000000.0, R.drawable.tangem2, "Binance"),
BinanceTestNet("BINANCE/test", "BNB", 100000000.0, R.drawable.tangem2, "Binance Testnet"),
Matic("MATIC", "MTX", 1.0, R.drawable.tangem2, "Matic"),
MaticTestNet("MATIC/test", "MTX", 1.0, R.drawable.tangem2, "Matic Testnet");
Stellar("XLM", "XLM", R.drawable.ic_logo_stellar, "Stellar"),
StellarTestNet("XLM/test", "XLM", R.drawable.ic_logo_stellar, "Stellar 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;
@ -35,6 +47,10 @@ public enum Blockchain {
return mOfficialName;
}
// public double getMultiplier() {
// return mMultiplier;
// }
public String getCurrency() {
return mCurrency;
}
@ -78,7 +94,6 @@ public enum Blockchain {
return resourceId;
}
//TODO - ???
public static int getLogoImageResource(String blockchainID, String symbolName) {
switch (blockchainID) {
case "BTC":

View file

@ -20,11 +20,7 @@ import java.util.Objects;
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.LogFileProvider";
// public static final String AUTHORITY = "com.tangem.LogFileProvider";
private static final String TAG = LogFileProvider.class.getSimpleName() + "-oF";
// UriMatcher used to match against incoming requests
private UriMatcher uriMatcher;
@ -43,15 +39,11 @@ public class LogFileProvider extends ContentProvider {
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
String LOG_TAG = CLASS_NAME + "-oF";
Log.v(TAG, "Called with uri: '" + uri + "'." + uri.getLastPathSegment());
Log.v(LOG_TAG,
"Called with uri: '" + uri + "'." + uri.getLastPathSegment());
// Check incoming Uri against the matcher
// check incoming Uri against the matcher
switch (uriMatcher.match(uri)) {
// If it returns 1 - then it matches the Uri defined in onCreate
@ -60,7 +52,7 @@ public class LogFileProvider extends ContentProvider {
// The desired file name is specified by the last segment of the
// path
// E.g.
// 'content://it.my.app.LogFileProvider/Test.txt'
// 'content://it.my.app.LogFileProvider/Test1.txt'
// Take this and build the path to the file
String fileLocation = getContext().getCacheDir() + File.separator
+ uri.getLastPathSegment();
@ -73,9 +65,8 @@ public class LogFileProvider extends ContentProvider {
// Otherwise unrecognised Uri
default:
Log.v(LOG_TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: "
+ uri.toString());
Log.v(TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}

View file

@ -3,7 +3,7 @@ package com.tangem.data;
import android.content.Context;
import android.util.Log;
import com.tangem.tangemcard.util.Util;
import com.tangem.card_common.util.Util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@ -226,7 +226,7 @@ public class Logger {
//
// verifyStoragePermissions(activity);
// initLogFile(activity.getApplicationContext());
// t.start();
// t.init();
// } catch (Exception e) {
// e.printStackTrace();
// }
@ -268,7 +268,7 @@ public class Logger {
// //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
// // check if we have write permission
// int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
//
// if (permission != PackageManager.PERMISSION_GRANTED) {

View file

@ -0,0 +1,44 @@
package com.tangem.data.dp
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import com.orhanobut.hawk.Hawk
import com.tangem.Constant
class PrefsManager {
companion object {
const val PREF_NAME = "tangem_access"
@SuppressLint("StaticFieldLeak")
private var instance: PrefsManager? = null
fun getInstance(): PrefsManager {
if (instance == null) {
instance = PrefsManager()
}
return instance as PrefsManager
}
}
private lateinit var context: Context
private lateinit var preferences: SharedPreferences
fun init(context: Context) {
this.context = context
preferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
Hawk.init(context).build()
}
val lastWalletAddress: String
get() = Hawk.get(Constant.PREF_LAST_WALLET_ADDRESS, "")
fun saveLastWalletAddress(value: String) {
Hawk.put(Constant.PREF_LAST_WALLET_ADDRESS, value)
}
fun clearLastWalletAddress() {
Hawk.delete(Constant.PREF_LAST_WALLET_ADDRESS)
}
}

View file

@ -3,7 +3,7 @@ package com.tangem.data.fingerprint;
import android.os.AsyncTask;
import android.widget.Toast;
import com.tangem.presentation.activity.PinSaveActivity;
import com.tangem.ui.activity.PinSaveActivity;
import com.tangem.wallet.R;
import java.lang.ref.WeakReference;

View file

@ -9,8 +9,9 @@ import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import com.tangem.Constant;
import com.tangem.tangemcard.android.data.PINStorage;
import com.tangem.presentation.activity.PinRequestActivity;
import com.tangem.card_android.android.data.PINStorage;
import com.tangem.ui.activity.PinRequestActivity;
import com.tangem.util.LOG;
import java.io.IOException;
import java.lang.ref.WeakReference;
@ -26,6 +27,8 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
public static final String TAG = StartFingerprintReaderTask.class.getSimpleName();
private WeakReference<PinRequestActivity> reference;
private KeyStore keyStore;
@ -64,16 +67,14 @@ public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected void onPostExecute(final Boolean success) {
PinRequestActivity pinRequestActivity = reference.get();
onCancelled();
if (!success) {
pinRequestActivity.doLog("Authentication failed!");
LOG.i(TAG, "Authentication failed!");
} else {
fingerprintHelper.startAuth(fingerprintManager, cryptoObject);
pinRequestActivity.doLog("Authenticate using fingerprint!");
LOG.i(TAG, "Authenticate using fingerprint!");
}
}
@ -85,9 +86,7 @@ public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
}
private boolean getKeyStore() {
PinRequestActivity pinRequestActivity = reference.get();
pinRequestActivity.doLog("Getting keystore...");
LOG.i(TAG, "Getting keystore...");
try {
keyStore = KeyStore.getInstance(Constant.KEYSTORE);
keyStore.load(null); // Create empty keystore
@ -101,9 +100,7 @@ public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
@TargetApi(Build.VERSION_CODES.M)
public boolean createNewKey(boolean forceCreate) {
PinRequestActivity pinRequestActivity = reference.get();
pinRequestActivity.doLog("Creating new key...");
LOG.i(TAG, "Creating new key...");
try {
if (forceCreate)
keyStore.deleteEntry(Constant.KEY_ALIAS);
@ -120,9 +117,9 @@ public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
);
generator.generateKey();
pinRequestActivity.doLog("Key created.");
LOG.i(TAG, "Key created.");
} else
pinRequestActivity.doLog("Key exists.");
LOG.i(TAG, "Key exists.");
return true;
} catch (Exception e) {
@ -133,9 +130,7 @@ public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
}
private boolean getCipher() {
PinRequestActivity pinRequestActivity = reference.get();
pinRequestActivity.doLog("Getting cipher...");
LOG.i(TAG, "Getting cipher...");
try {
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);
return true;
@ -150,7 +145,7 @@ public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
private boolean initCipher(int mode) {
PinRequestActivity pinRequestActivity = reference.get();
pinRequestActivity.doLog("Initializing cipher...");
LOG.i(TAG, "Initializing cipher...");
try {
keyStore.load(null);
SecretKey keyspec = (SecretKey) keyStore.getKey(Constant.KEY_ALIAS, null);
@ -181,9 +176,7 @@ public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
@TargetApi(Build.VERSION_CODES.M)
private boolean initCryptObject() {
PinRequestActivity pinRequestActivity = reference.get();
pinRequestActivity.doLog("Initializing crypt object...");
LOG.i(TAG, "Initializing crypt object...");
try {
cryptoObject = new FingerprintManager.CryptoObject(cipher);
return true;
@ -193,5 +186,4 @@ public class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
return false;
}
}

View file

@ -0,0 +1,106 @@
package com.tangem.data.local
import android.content.Context
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.tangem.card_common.data.TangemCard
import java.io.File
import java.util.*
import kotlin.collections.HashMap
class PendingTransactionsStorage
(
val context: Context
) {
private lateinit var cardTransactions: MutableMap<String, CardTransactionsInfo>
private val transactionsFile: File = File(context.filesDir, "transactions.json")
private var cacheDir: File? = null
init {
cacheDir = File(context.filesDir, "artworks")
if (!cacheDir!!.exists())
cacheDir!!.mkdirs()
if (transactionsFile.exists()) {
try {
transactionsFile.bufferedReader().use { cardTransactions = Gson().fromJson(it, object : TypeToken<HashMap<String, CardTransactionsInfo>>() {}.type) }
} catch (e: Exception) {
e.printStackTrace()
cardTransactions = HashMap()
}
} else {
cardTransactions = HashMap()
}
}
private fun clearExpired()
{
for(cardId in cardTransactions.keys) {
cardTransactions[cardId]!!.transactions.removeAll { it.isExpired() }
}
cardTransactions.entries.removeAll { it.value.isEmpty() }
}
fun save() {
clearExpired()
val sTransactions = Gson().toJson(cardTransactions)
transactionsFile.bufferedWriter().use { it.write(sTransactions) }
}
fun putTransaction(card: TangemCard, txId: String, expireTimeoutInSeconds: Int) {
val sendDate=Date()
val calendar=Calendar.getInstance()
calendar.time=sendDate
calendar.add(Calendar.SECOND, expireTimeoutInSeconds)
val expireDate=calendar.time
putTransaction(card.cidDescription, txId, sendDate, expireDate)
}
private fun putTransaction(cardId: String, txId: String, sendDate: Date, expireDate: Date) {
val transactionInfo = TransactionInfo(
txId, sendDate, expireDate
)
if (cardTransactions[cardId] == null) {
cardTransactions[cardId] = CardTransactionsInfo(arrayListOf())
}
cardTransactions[cardId]?.transactions?.add(transactionInfo)
save()
}
fun getTransactions(card: TangemCard): CardTransactionsInfo? {
clearExpired()
return cardTransactions[card.cidDescription]
}
fun hasTransactions(card: TangemCard): Boolean {
val cardTransactionsInfo=getTransactions(card)
if( cardTransactionsInfo!=null ) return cardTransactionsInfo.transactions.count()>0
return false
}
fun removeTransaction(card: TangemCard, txId: String)
{
cardTransactions[card.cidDescription]?.transactions?.removeAll { it.tx==txId }
save()
}
data class TransactionInfo(
val tx: String,
val sendDate: Date,
val expireDate: Date
) {
fun isExpired(): Boolean {
return Date().after(expireDate)
}
}
data class CardTransactionsInfo(
var transactions: MutableList<TransactionInfo>
) {
fun isEmpty(): Boolean {
return transactions.count()==0
}
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.data.network;
import com.tangem.data.network.model.AdaliteBody;
import com.tangem.data.network.model.AdaliteResponse;
import com.tangem.data.network.model.AdaliteResponseUtxo;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface AdaliteApi {
@GET(ServerApiAdalite.ADALITE_ADDRESS)
Call<AdaliteResponse> adaliteAddress(@Path("address") String address);
@Headers("Content-Type: application/json")
@POST(ServerApiAdalite.ADALITE_UNSPENT_OUTPUTS)
Call<AdaliteResponseUtxo> adaliteUnspent(@Body String address);
// @GET(ServerApiAdalite.ADALITE_TRANSACTION)
// Call<AdaliteResponse> adaliteTransaction(@Path("txId") String txId);
@Headers("Content-Type: application/json")
@POST(ServerApiAdalite.ADALITE_SEND)
Call<List> adaliteSend(@Body AdaliteBody adaliteBody);
}

View file

@ -0,0 +1,13 @@
package com.tangem.data.network;
import com.tangem.data.network.model.BinanceFees;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
public interface BinanceApi {
@GET("fees")
Call<List<BinanceFees>> binanceFees();
}

View file

@ -0,0 +1,25 @@
package com.tangem.data.network;
import com.tangem.data.network.model.BlockcypherBody;
import com.tangem.data.network.model.BlockcypherResponse;
import com.tangem.data.network.model.BlockcypherFee;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface BlockcypherApi {
@GET(Server.ApiBlockcypher.Method.MAIN)
Call<BlockcypherFee> blockcypherMain(@Path("blockchain") String blockchain);
@GET(Server.ApiBlockcypher.Method.ADDRESS)
Call<BlockcypherResponse> blockcypherAddress(@Path("blockchain") String blockchain, @Path("address") String address);
@Headers("Content-Type: application/json")
@POST(Server.ApiBlockcypher.Method.PUSH)
Call<BlockcypherResponse> blockcypherPush(@Path("blockchain") String blockchain, @Body BlockcypherBody blockcypherBody, @Query("token") String token);
}

View file

@ -0,0 +1,7 @@
package com.tangem.data.network
enum class BlockcypherToken(val token: String) {
T_001("aa8184b0e0894b88a5688e01b3dc1e82"),
T_002("56c4ca23c6484c8f8864c32fde4def8d"),
T_003("66a8a37c5e9d4d2c9bb191acfe7f93aa")
}

View file

@ -2,12 +2,14 @@ package com.tangem.data.network;
import com.tangem.data.network.model.RateInfoResponse;
import java.util.List;
import io.reactivex.Observable;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Query;
public interface CoinmarketApi {
@GET(Server.ApiCoinmarket.Method.V1_TICKER_CONVERT)
Observable<List<RateInfoResponse>> getRateInfoList();
@Headers("X-CMC_PRO_API_KEY: f6622117-c043-47a0-8975-9d673ce484de")
@GET(Server.ApiCoinmarket.Method.PRICE_CONVERSION)
Observable<RateInfoResponse> getRateInfo(@Query("amount") int amount, @Query("symbol") String cryptoId);
}

View file

@ -11,7 +11,7 @@ import com.google.gson.annotations.SerializedName;
import com.google.gson.internal.LinkedTreeMap;
import com.jakewharton.retrofit2.adapter.rxjava2.HttpException;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.tangemcard.util.Util;
import com.tangem.card_common.util.Util;
import com.tangem.wallet.R;
import java.io.IOException;

View file

@ -0,0 +1,32 @@
package com.tangem.data.network;
import com.tangem.data.network.model.InsightBody;
import com.tangem.data.network.model.InsightResponse;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface InsightApi {
@GET(ServerApiInsight.INSIGHT_ADDRESS)
Call<InsightResponse> insightAddress(@Path("address") String address);
@GET(ServerApiInsight.INSIGHT_UNSPENT_OUTPUTS)
Call<List<InsightResponse>> insightUnspent(@Path("address") String address);
@GET(ServerApiInsight.INSIGHT_TRANSACTION)
Call<InsightResponse> insightTransaction(@Path("txId") String txId);
@GET(ServerApiInsight.INSIGHT_FEE)
Call<InsightResponse> insightFee();
@Headers("Content-Type: application/json")
@POST(ServerApiInsight.INSIGHT_SEND)
Call<InsightResponse> insightSend(@Body InsightBody body );
}

View file

@ -6,7 +6,7 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.tangemcard.util.Util;
import com.tangem.card_common.util.Util;
import com.tangem.wallet.R;
import org.spongycastle.util.encoders.Base64;

View file

@ -0,0 +1,15 @@
package com.tangem.data.network;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface MaticApi {
@Headers("Content-Type: application/json")
@POST(Server.ApiMaticTesnet.Method.MAIN)
Call<InfuraResponse> matic(@Body InfuraBody body);
}

View file

@ -0,0 +1,15 @@
package com.tangem.data.network;
import com.tangem.data.network.model.RippleBody;
import com.tangem.data.network.model.RippleResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface RippleApi {
@Headers("Content-Type: application/json")
@POST("./")
Call<RippleResponse> ripple(@Body RippleBody body);
}

View file

@ -0,0 +1,15 @@
package com.tangem.data.network;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Headers;
import retrofit2.http.POST;
public interface RootstockApi {
@Headers("Content-Type: application/json")
@POST(Server.ApiRootstock.Method.MAIN)
Call<InfuraResponse> rootstock(@Body InfuraBody body);
}

View file

@ -17,7 +17,7 @@ public class Server {
public static final String URL_COINMARKET = ServerURL.API_COINMARKETCAP;
public static class Method {
static final String V1_TICKER_CONVERT = URL_COINMARKET + "v1/ticker/?convert=USD&lmit=10";
static final String PRICE_CONVERSION = URL_COINMARKET + "v1/tools/price-conversion";
}
}
@ -28,7 +28,29 @@ public class Server {
public static final String URL_INFURA = ServerURL.API_INFURA;
public static class Method {
static final String MAIN = URL_INFURA + "613a0b14833145968b1f656240c7d245";
static final String MAIN = URL_INFURA + "v3/613a0b14833145968b1f656240c7d245";
}
}
/**
* https://public-node.rsk.co/
*/
public static class ApiRootstock {
public static final String URL_ROOTSTOCK = ServerURL.API_ROOTSTOCK;
public static class Method {
static final String MAIN = URL_ROOTSTOCK;
}
}
/**
* https://testnet2.matic.network
*/
public static class ApiMaticTesnet {
public static final String URL_MATIC_TESTNET = ServerURL.API_MATIC_TESTNET ;
public static class Method {
static final String MAIN = URL_MATIC_TESTNET;
}
}
@ -45,4 +67,42 @@ public class Server {
}
}
/**
* https://dex.binance.org/
*/
public static class ApiBinance {
public static final String URL_BINANCE = ServerURL.API_BINANCE;
public static class Method {
public static final String API_V1 = URL_BINANCE + "api/v1/";
}
}
/**
* https://testnet-dex.binance.org/
*/
public static class ApiBinanceTestnet {
public static final String URL_BINANCE_TESTNET = ServerURL.API_BINANCE_TESTNET;
public static class Method {
public static final String API_V1 = URL_BINANCE_TESTNET + "api/v1/";
}
}
/**
* https://api.blockcypher.com/
*/
public static class ApiBlockcypher {
public static final String URL_BLOCKCYPHER = ServerURL.API_BLOCKCYPHER;
static final String V1_MAIN = "v1/{blockchain}/main";
public static class Method {
static final String MAIN = URL_BLOCKCYPHER + V1_MAIN;
static final String ADDRESS = MAIN + "/addrs/{address}?unspentOnly=true&includeScript=true";
static final String PUSH = MAIN + "/txs/push";
}
}
}

View file

@ -0,0 +1,144 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.data.network.model.AdaliteBody;
import com.tangem.data.network.model.AdaliteResponse;
import com.tangem.data.network.model.AdaliteResponseUtxo;
import java.util.List;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class ServerApiAdalite {
private static String TAG = ServerApiAdalite.class.getSimpleName();
public static final String ADALITE_ADDRESS = "/api/addresses/summary/{address}";
public static final String ADALITE_UNSPENT_OUTPUTS = "/api/bulk/addresses/utxo";
// public static final String ADALITE_TRANSACTION = "/api/txs/raw/{txId}}";
public static final String ADALITE_SEND = "/api/v2/txs/signed";
private int requestsCount = 0;
public static String lastNode;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, AdaliteResponse adaliteResponse);
void onSuccess(String method, AdaliteResponseUtxo adaliteResponseUtxo);
void onSuccess(String method, List listResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, String wallet, String tx) {
requestsCount++;
String adaliteURL = "https://explorer2.adalite.io"; //TODO: make random selection
this.lastNode = adaliteURL; //TODO: show node instead of URL
Retrofit retrofitAdalite = new Retrofit.Builder()
.baseUrl(adaliteURL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
AdaliteApi adaliteApi = retrofitAdalite.create(AdaliteApi.class);
switch (method) {
case ADALITE_ADDRESS:
Call<AdaliteResponse> addressCall = adaliteApi.adaliteAddress(wallet);
addressCall.enqueue(new Callback<AdaliteResponse>() {
@Override
public void onResponse(@NonNull Call<AdaliteResponse> call, @NonNull Response<AdaliteResponse> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<AdaliteResponse> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
case ADALITE_UNSPENT_OUTPUTS:
Call<AdaliteResponseUtxo> outputsCall = adaliteApi.adaliteUnspent("[\"" + wallet + "\"]");
outputsCall.enqueue(new Callback<AdaliteResponseUtxo>() {
@Override
public void onResponse(@NonNull Call<AdaliteResponseUtxo> call, @NonNull Response<AdaliteResponseUtxo> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<AdaliteResponseUtxo> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
case ADALITE_SEND:
Call<List> sendCall = adaliteApi.adaliteSend(new AdaliteBody(tx));
sendCall.enqueue(new Callback<List>() {
@Override
public void onResponse(@NonNull Call<List> call, @NonNull Response<List> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<List> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
default:
responseListener.onFail(method, "undeclared method");
Log.e(TAG, "requestData " + method + " onFailure - undeclared method");
break;
}
}
}

View file

@ -0,0 +1,126 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.wallet.TangemContext;
import com.tangem.wallet.Transaction;
import com.tangem.wallet.binance.BinanceData;
import com.tangem.wallet.binance.client.BinanceDexApiRestClient;
import com.tangem.wallet.binance.client.domain.Account;
import com.tangem.wallet.binance.client.domain.Balance;
import com.tangem.wallet.binance.client.domain.TransactionMetadata;
import com.tangem.wallet.binance.client.encoding.message.TransactionRequestAssemblerExtSign;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.observers.DefaultObserver;
import io.reactivex.schedulers.Schedulers;
import okhttp3.RequestBody;
public class ServerApiBinance {
private static String TAG = ServerApiBinance.class.getSimpleName();
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess();
void onFail();
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void getBalance(TangemContext ctx, BinanceDexApiRestClient client) {
Log.i(TAG, "new getBalance request");
Observable<Account> balanceObservable = Observable.just(new Account())
.map(account -> client.getAccount(ctx.getCoinData().getWallet()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
balanceObservable.subscribe(new DefaultObserver<Account>() {
@Override
public void onNext(Account account) {
Log.i(TAG, "getBalance onNext");
// account = client.getAccount(ctx.getCoinData().getWallet());
BinanceData binanceData = (BinanceData) ctx.getCoinData();
for (Balance balance : account.getBalances()) {
if (balance.getSymbol().equals("BNB")) {
binanceData.setBalanceReceived(true);
binanceData.setBalance(balance.getFree());
break;
}
}
if (!binanceData.isBalanceReceived()) {
binanceData.setBalanceReceived(true);
binanceData.setBalance("0");
}
binanceData.setAccountNumber(account.getAccountNumber());
binanceData.setSequence(account.getSequence());
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "getBalance onError" + e.getMessage());
if (e.getMessage().contains("code=404")) {
((BinanceData)ctx.getCoinData()).setError404(true);
responseListener.onFail();
} else {
e.printStackTrace();
ctx.setError(e.getMessage());
responseListener.onFail();
}
}
@Override
public void onComplete() {
Log.i(TAG, "getBalance onComplete");
responseListener.onSuccess();
}
});
}
public void sendTransaction (byte[] txForSend, BinanceDexApiRestClient client) {
Log.i(TAG, "new sendTransaction request");
RequestBody requestBody = TransactionRequestAssemblerExtSign.createRequestBody(txForSend);
Observable<List<TransactionMetadata>> sendObservable = Observable.just(new ArrayList<>())
.map(metadatas -> client.broadcastNoWallet(requestBody, true))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
sendObservable.subscribe(new DefaultObserver<List<TransactionMetadata>>() {
@Override
public void onNext(List<TransactionMetadata> metadatas ) {
// RequestBody requestBody = TransactionRequestAssemblerExtSign.createRequestBody(txForSend);
// List<TransactionMetadata> metadatas = client.broadcastNoWallet(requestBody, true);
if (!metadatas.isEmpty() && metadatas.get(0).isOk()) {
responseListener.onSuccess();
} else {
Log.e(TAG, "Transaction send error");
responseListener.onFail();
}
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "sendTransaction onError" + e.getMessage());
e.printStackTrace();
responseListener.onFail();
}
@Override
public void onComplete() {
Log.i(TAG, "sendTransaction onComplete");
}
});
}
}

View file

@ -0,0 +1,128 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.model.BlockcypherBody;
import com.tangem.data.network.model.BlockcypherFee;
import com.tangem.data.network.model.BlockcypherResponse;
import java.util.Random;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiBlockcypher {
private static String TAG = ServerApiRipple.class.getSimpleName();
public static final String BLOCKCYPHER_ADDRESS = "blockcypher_address";
public static final String BLOCKCYPHER_FEE = "blockcypher_fee";
public static final String BLOCKCYPHER_SEND = "blockcypher_send";
private int requestsCount = 0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, BlockcypherResponse blockcypherResponse);
void onSuccess(String method, BlockcypherFee blockcypherFee);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String blockchainID, String method, String wallet, String tx) {
requestsCount++;
String blockchain = blockchainID.toLowerCase();
BlockcypherApi blockcypherApi = App.Companion.getNetworkComponent().getRetrofitBlockcypher().create(BlockcypherApi.class);
switch (method) {
case BLOCKCYPHER_ADDRESS:
Call<BlockcypherResponse> addressCall = blockcypherApi.blockcypherAddress(blockchain, wallet);
addressCall.enqueue(new Callback<BlockcypherResponse>() {
@Override
public void onResponse(@NonNull Call<BlockcypherResponse> call, @NonNull Response<BlockcypherResponse> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<BlockcypherResponse> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
case BLOCKCYPHER_FEE:
Call<BlockcypherFee> feeCall = blockcypherApi.blockcypherMain(blockchain);
feeCall.enqueue(new Callback<BlockcypherFee>() {
@Override
public void onResponse(@NonNull Call<BlockcypherFee> call, @NonNull Response<BlockcypherFee> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<BlockcypherFee> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
case BLOCKCYPHER_SEND:
BlockcypherToken blockcypherToken = BlockcypherToken.values()[new Random().nextInt(BlockcypherToken.values().length)];
Call<BlockcypherResponse> sendCall = blockcypherApi.blockcypherPush(blockchain, new BlockcypherBody(tx), blockcypherToken.getToken());
sendCall.enqueue(new Callback<BlockcypherResponse>() {
@Override
public void onResponse(@NonNull Call<BlockcypherResponse> call, @NonNull Response<BlockcypherResponse> response) {
if (response.code() == 201) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<BlockcypherResponse> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
break;
default:
responseListener.onFail(method, "undeclared method");
Log.e(TAG, "requestData " + method + " onFailure - undeclared method");
break;
}
}
}

View file

@ -1,7 +1,7 @@
package com.tangem.data.network;
import android.annotation.SuppressLint;
import android.support.annotation.NonNull;
import androidx.annotation.NonNull;
import android.util.Log;
import com.tangem.App;
@ -24,19 +24,19 @@ public class ServerApiCommon {
public static final int ESTIMATE_FEE_PRIORITY = 2;
public static final int ESTIMATE_FEE_NORMAL = 3;
public static final int ESTIMATE_FEE_MINIMAL = 6;
private EstimateFeeListener estimateFeeListener;
private EstimatedFeeListener estimatedFeeListener;
public interface EstimateFeeListener {
public interface EstimatedFeeListener {
void onSuccess(int blockCount, String estimateFeeResponse);
void onFail(int blockCount, String message);
}
public void setEstimateFee(EstimateFeeListener listener) {
estimateFeeListener = listener;
public void setBtcEstimatedFeeListener(EstimatedFeeListener listener) {
estimatedFeeListener = listener;
}
public void estimateFee(int blockCount) {
EstimatefeeApi estimatefeeApi = App.getNetworkComponent().getRetrofitEstimatefee().create(EstimatefeeApi.class);
public void requestBtcEstimatedFee(int blockCount) {
EstimatefeeApi estimatefeeApi = App.Companion.getNetworkComponent().getRetrofitEstimatefee().create(EstimatefeeApi.class);
Call<String> call;
switch (blockCount) {
@ -60,17 +60,17 @@ public class ServerApiCommon {
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
if (response.code() == 200) {
estimateFeeListener.onSuccess(blockCount, response.body());
Log.i(TAG, "estimateFee onResponse " + response.code() + " " + response.body());
estimatedFeeListener.onSuccess(blockCount, response.body());
Log.i(TAG, "requestBtcEstimatedFee onResponse " + response.code() + " " + response.body());
} else
estimateFeeListener.onFail(blockCount, response.body());
Log.e(TAG, "estimateFee onResponse " + response.code());
estimatedFeeListener.onFail(blockCount, response.body());
Log.e(TAG, "requestBtcEstimatedFee onResponse " + response.code());
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
estimateFeeListener.onFail(blockCount, t.getMessage());
Log.e(TAG, "estimateFee onFailure " + t.getMessage());
estimatedFeeListener.onFail(blockCount, t.getMessage());
Log.e(TAG, "requestBtcEstimatedFee onFailure " + t.getMessage());
}
});
}
@ -79,33 +79,51 @@ public class ServerApiCommon {
* HTTP
* Used in Crypto-currency course
*/
private RateInfoDataListener rateInfoDataListener;
private RateInfoListener rateInfoListener;
public interface RateInfoDataListener {
public interface RateInfoListener {
void onSuccess(RateInfoResponse rateInfoResponse);
void onFail(String message);
}
public void setRateInfoData(RateInfoDataListener listener) {
rateInfoDataListener = listener;
public void setRateInfoListener(RateInfoListener listener) {
rateInfoListener = listener;
}
@SuppressLint("CheckResult")
public void rateInfoData(String cryptoId) {
CoinmarketApi coinmarketApi = App.getNetworkComponent().getRetrofitCoinmarketcap().create(CoinmarketApi.class);
public void requestRateInfo(String cryptoId) {
CoinmarketApi coinmarketApi = App.Companion.getNetworkComponent().getRetrofitCoinmarketcap().create(CoinmarketApi.class);
coinmarketApi.getRateInfoList()
// Call<RateInfoResponse> call = coinmarketApi.getRateInfo(1, cryptoId);
//
// call.enqueue(new Callback<RateInfoResponse>() {
// @Override
// public void onResponse(@NonNull Call<RateInfoResponse> call, @NonNull Response<RateInfoResponse> response) {
// if (response.code() == 200) {
// rateInfoListener.onSuccess(response.body());
// Log.i(TAG, "coinmarketcap onResponse " + response.code());
// } else {
// rateInfoListener.onFail("Rate info error:" + String.valueOf(response.code()));
// Log.e(TAG, "coinmarketcap onResponse " + response.code());
// }
// }
//
// @Override
// public void onFailure(@NonNull Call<RateInfoResponse> call, @NonNull Throwable t) {
// rateInfoListener.onFail(String.valueOf(t.getMessage()));
// Log.e(TAG, "coinmarketcap onFailure " + t.getMessage());
// }
// });
coinmarketApi.getRateInfo(1, cryptoId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(rateInfoModelList -> {
if (!rateInfoModelList.isEmpty()) {
for (RateInfoResponse rateInfoMode : rateInfoModelList) {
if (rateInfoMode.getId().equals(cryptoId)) {
rateInfoDataListener.onSuccess(rateInfoMode);
Log.i(TAG, "rateInfoData " + cryptoId + " onResponse " + "200");
}
}
} else
Log.e(TAG, "rateInfoData " + cryptoId + " onResponse " + "Empty");
.subscribe(rateInfoResponse -> {
if (rateInfoResponse.getData().getQuote().getUsd().getPrice() != null) {
rateInfoListener.onSuccess(rateInfoResponse);
Log.i(TAG, "coinmarketcap onResponse " + 200);
} else {
rateInfoListener.onFail("Rate info error: wrong response");
}
},
// handle error
Throwable::printStackTrace);
@ -126,7 +144,7 @@ public class ServerApiCommon {
}
public void requestLastVersion() {
UpdateVersionApi updateVersionApi = App.getNetworkComponent().getRetrofitGithubusercontent().create(UpdateVersionApi.class);
UpdateVersionApi updateVersionApi = App.Companion.getNetworkComponent().getRetrofitGitHubUserContent().create(UpdateVersionApi.class);
Call<ResponseBody> call = updateVersionApi.getLastVersion();
call.enqueue(new Callback<ResponseBody>() {

View file

@ -3,13 +3,12 @@ package com.tangem.data.network;
import android.util.Log;
import com.tangem.App;
import com.tangem.domain.wallet.TangemContext;
import com.tangem.wallet.TangemContext;
import com.tangem.data.Blockchain;
import com.tangem.domain.wallet.bch.BitcoinCashNode;
import com.tangem.domain.wallet.btc.BitcoinNode;
import com.tangem.domain.wallet.btc.BitcoinNodeTestNet;
import com.tangem.domain.wallet.ltc.LitecoinNode;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.wallet.bch.BitcoinCashNode;
import com.tangem.wallet.btc.BitcoinNode;
import com.tangem.wallet.btc.BitcoinNodeTestNet;
import com.tangem.wallet.ltc.LitecoinNode;
import com.tangem.wallet.R;
import java.io.BufferedReader;
@ -24,7 +23,6 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
@ -47,22 +45,23 @@ import io.reactivex.schedulers.Schedulers;
/**
* Request processor for Electrum Api
* Every request live cycle:
* 1. In application create request and call {@link ServerApiElectrum}.electrumRequestData(..)
* 1. In application create request and call {@link ServerApiElectrum}.requestData(..)
* 2. Try send every request for max 4 times,
* 3. If all 4 times fail call DefaultObserver<ElectrumRequest>.onError (defined in .electrumRequestData(..)) and than
* {@link ElectrumRequestDataListener}.onFail(...) callback
* 3. If all 4 times fail call DefaultObserver<ElectrumRequest>.onError (defined in .requestData(..)) and than
* {@link ResponseListener}.onFail(...) callback
* Error can be acquired with {@link ElectrumRequest}.getError() method
* 4. If request network communication finished successfully then call DefaultObserver<ElectrumRequest>.onComplete (defined in .electrumRequestData) and than
* {@link ElectrumRequestDataListener}.onSuccess(...) callback
* 4. If request network communication finished successfully then call DefaultObserver<ElectrumRequest>.onComplete (defined in .requestData) and than
* {@link ResponseListener}.onSuccess(...) callback
*/
public class ServerApiElectrum {
public static final String ERROR_STARTS_WITH_CODE_32601 = "{\"code\":-32601,";
private static String TAG = ServerApiElectrum.class.getSimpleName();
/**
* TCP, SSL
* Used in BTC, BCH
*/
private ElectrumRequestDataListener electrumRequestDataListener;
private ResponseListener responseListener;
private String host;
private int port;
@ -76,7 +75,7 @@ public class ServerApiElectrum {
/**
* Interface for notification every request result
*/
public interface ElectrumRequestDataListener {
public interface ResponseListener {
/**
* Notify that request processing was successful
@ -94,8 +93,8 @@ public class ServerApiElectrum {
* Set notificaion listener
* @param listener
*/
public void setElectrumRequestData(ElectrumRequestDataListener listener) {
electrumRequestDataListener = listener;
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
@ -104,7 +103,7 @@ public class ServerApiElectrum {
* @param ctx
* @param electrumRequest
*/
public void electrumRequestData(TangemContext ctx, ElectrumRequest electrumRequest) {
public void requestData(TangemContext ctx, ElectrumRequest electrumRequest) {
requestsCount++;
Log.i(TAG, String.format("New request[%d]: %s", requestsCount,electrumRequest.getMethod()));
Observable<ElectrumRequest> checkElectrumDataObserver = Observable.just(electrumRequest)
@ -129,20 +128,20 @@ public class ServerApiElectrum {
@Override
public void onNext(ElectrumRequest v) {
if (electrumRequest.answerData != null) {
Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onNext != null");
Log.i(TAG, "requestData " + electrumRequest.getMethod() + " onNext != null");
} else {
Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onNext == null");
Log.e(TAG, "requestData " + electrumRequest.getMethod() + " onNext == null");
}
}
@Override
public void onError(Throwable e) {
requestsCount--;
Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onError " + e.getMessage());
Log.e(TAG, "requestData " + electrumRequest.getMethod() + " onError " + e.getMessage());
Log.e(TAG, String.format("%d requests left in processing",requestsCount));
electrumRequest.setError(ctx.getString(R.string.cannot_obtain_data_from_blockchain));
//setErrorOccurred(e.getMessage());//;
electrumRequestDataListener.onFail(electrumRequest);
responseListener.onFail(electrumRequest);
}
/**
@ -152,16 +151,16 @@ public class ServerApiElectrum {
public void onComplete() {
requestsCount--;
if (electrumRequest.answerData != null) {
Log.i(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onComplete, answerData!=null");
Log.i(TAG, "requestData " + electrumRequest.getMethod() + " onComplete, answerData!=null");
} else {
Log.e(TAG, "electrumRequestData " + electrumRequest.getMethod() + " onComplete, answerData==null");
Log.e(TAG, "requestData " + electrumRequest.getMethod() + " onComplete, answerData==null");
}
Log.e(TAG, String.format("%d requests left in processing",requestsCount));
if (electrumRequest.answerData != null && electrumRequest.getError()==null) {
electrumRequestDataListener.onSuccess(electrumRequest);
responseListener.onSuccess(electrumRequest);
} else {
// if( error==null || error.isEmpty() ) setErrorOccurred(ctx.getString(R.string.cannot_obtain_data_from_blockchain));
electrumRequestDataListener.onFail(electrumRequest);
responseListener.onFail(electrumRequest);
}
}
@ -231,7 +230,7 @@ public class ServerApiElectrum {
private void doElectrumRequestTcp(ElectrumRequest electrumRequest, String host, int port) {
try {
Socket socket = App.getNetworkComponent().getSocket();
Socket socket = App.Companion.getNetworkComponent().getSocket();
socket.setSoTimeout(3000);
Log.i(TAG, "Start process "+electrumRequest.getMethod()+" @ "+host + ":" + port);
socket.connect(new InetSocketAddress(InetAddress.getByName(host), port));
@ -250,15 +249,21 @@ public class ServerApiElectrum {
electrumRequest.port = port;
if (electrumRequest.answerData != null) {
Log.i(TAG, ">> " + electrumRequest.answerData);
if( (electrumRequest.getError()!=null && electrumRequest.getError().startsWith(ERROR_STARTS_WITH_CODE_32601)) )
{
// method unknown error???
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain));
electrumRequest.answerData=null;
}
} else {
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_answer));
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_answer));
Log.i(TAG, ">> <NULL>");
}
} catch (ConnectException e) {
//e.printStackTrace();
//electrumRequestDataListener.onFail(e.getMessage());
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_connection));
//responseListener.onFail(e.getMessage());
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_connection));
Log.e(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " ConnectException " + e.getMessage());
} finally {
Log.i(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " socket.close");
@ -269,13 +274,13 @@ public class ServerApiElectrum {
{
e.printStackTrace();
Log.e(TAG,"Can't close socket");
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error));
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error));
}
}
} catch (IOException e) {
//e.printStackTrace();
//electrumRequestDataListener.onFail(e.getMessage());
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error));
//responseListener.onFail(e.getMessage());
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error));
Log.e(TAG, "doElectrumRequestTcp " + electrumRequest.getMethod() + " IOException " + e.getMessage());
}
}
@ -312,14 +317,16 @@ public class ServerApiElectrum {
// install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
SSLSocket sslSocket;
Socket sslSocket = new Socket();
List<ElectrumRequest> result = new ArrayList<>();
Collections.addAll(result, electrumRequest);
try {
Log.i(TAG, host + " " + port);
sslSocket = (SSLSocket) sf.createSocket(host, port);
sslSocket.connect(new InetSocketAddress(host,port), 3000);
sslSocket.setSoTimeout(3000);
sslSocket = sf.createSocket(sslSocket, host, port, true);
try {
OutputStream os = sslSocket.getOutputStream();
OutputStreamWriter out = new OutputStreamWriter(os, "UTF-8");
@ -333,14 +340,20 @@ public class ServerApiElectrum {
electrumRequest.answerData = in.readLine();
if (electrumRequest.answerData != null) {
Log.i(TAG, ">> " + electrumRequest.answerData);
if( (electrumRequest.getError()!=null && electrumRequest.getError().startsWith(ERROR_STARTS_WITH_CODE_32601)) )
{
// method unknown error???
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain));
electrumRequest.answerData=null;
}
} else {
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_answer));
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_answer));
Log.i(TAG, ">> <NULL>");
}
} catch (ConnectException e) {
e.printStackTrace();
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_connection));
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_no_connection));
Log.e(TAG, "doElectrumRequestSsl " + electrumRequest.getMethod() + " ConnectException " + e.getMessage());
} finally {
Log.i(TAG, "doElectrumRequestSsl " + electrumRequest.getMethod() + " socket.close");
@ -349,7 +362,7 @@ public class ServerApiElectrum {
}
catch (Exception e)
{
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error));
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error));
e.printStackTrace();
Log.e(TAG, "Can't close ssl socket");
}
@ -357,11 +370,11 @@ public class ServerApiElectrum {
} catch (IOException e) {
e.printStackTrace();
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error));
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain_communication_error));
Log.e(TAG, "doElectrumRequestSsl " + electrumRequest.getMethod() + " IOException " + e.getMessage());
}
} catch (NoSuchAlgorithmException | KeyManagementException e) {
electrumRequest.setError(App.getInstance().getString(R.string.cannot_obtain_data_from_blockchain));
electrumRequest.setError(App.Companion.getInstance().getString(R.string.cannot_obtain_data_from_blockchain));
Log.e(TAG, e.getMessage());
}
}

View file

@ -1,6 +1,6 @@
package com.tangem.data.network;
import android.support.annotation.NonNull;
import androidx.annotation.NonNull;
import android.util.Log;
import com.tangem.App;
@ -38,21 +38,21 @@ public class ServerApiInfura {
return requestsCount <= 0;
}
private InfuraBodyListener infuraBodyListener;
private ResponseListener responseListener;
public interface InfuraBodyListener {
public interface ResponseListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setInfuraResponse(InfuraBodyListener listener) {
infuraBodyListener = listener;
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void infura(String method, int id, String wallet, String contract, String tx) {
public void requestData(String method, int id, String wallet, String contract, String tx) {
requestsCount++;
InfuraApi infuraApi = App.getNetworkComponent().getRetrofitInfura().create(InfuraApi.class);
InfuraApi infuraApi = App.Companion.getNetworkComponent().getRetrofitInfura().create(InfuraApi.class);
InfuraBody infuraBody;
switch (method) {
@ -86,18 +86,18 @@ public class ServerApiInfura {
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
if (response.code() == 200) {
requestsCount--;
infuraBodyListener.onSuccess(method, response.body());
Log.i(TAG, "infura " + method + " onResponse " + response.code());
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
infuraBodyListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "infura " + method + " onResponse " + response.code());
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
infuraBodyListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "infura " + method + " onFailure " + t.getMessage());
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}

View file

@ -0,0 +1,129 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.data.network.model.InsightBody;
import com.tangem.data.network.model.InsightResponse;
import java.util.List;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ServerApiInsight {
private static String TAG = ServerApiInsight.class.getSimpleName();
public static final String INSIGHT_ADDRESS = "/addr/{address}";
public static final String INSIGHT_UNSPENT_OUTPUTS = "/addr/{address}/utxo";
public static final String INSIGHT_TRANSACTION = "/rawtx/{txId}";
public static final String INSIGHT_FEE = "/utils/estimatefee?nbBlocks=2,3,6";
public static final String INSIGHT_SEND = "/tx/send";
private int requestsCount = 0;
public static String lastNode;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, InsightResponse insightResponse);
void onSuccess(String method, List<InsightResponse> utxoList);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, String wallet, String tx) {
requestsCount++;
String insightURL = "http://130.185.109.17:3001/insigth-api"; //TODO: make random selection
this.lastNode = insightURL; //TODO: show node instead of URL
Retrofit retrofitInsight = new Retrofit.Builder()
.baseUrl(insightURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
// InsightApi insightApi = App.getNetworkComponent().getRetrofitInsight(insightURL).create(InsightApi.class);
InsightApi insightApi = retrofitInsight.create(InsightApi.class);
if (method.equals(INSIGHT_UNSPENT_OUTPUTS)) {
Call<List<InsightResponse>> call = insightApi.insightUnspent(wallet);
call.enqueue(new Callback<List<InsightResponse>>() {
@Override
public void onResponse(@NonNull Call<List<InsightResponse>> call, @NonNull Response<List<InsightResponse>> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<List<InsightResponse>> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
} else {
Call<InsightResponse> call;
switch (method) {
case INSIGHT_ADDRESS:
call = insightApi.insightAddress(wallet);
break;
case INSIGHT_TRANSACTION:
call = insightApi.insightTransaction(tx);
break;
case INSIGHT_FEE:
call = insightApi.insightFee();
break;
case INSIGHT_SEND:
call = insightApi.insightSend(new InsightBody(tx));
break;
default:
call = insightApi.insightAddress(wallet);
break;
}
call.enqueue(new Callback<InsightResponse>() {
@Override
public void onResponse(@NonNull Call<InsightResponse> call, @NonNull Response<InsightResponse> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InsightResponse> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}
}

View file

@ -0,0 +1,105 @@
package com.tangem.data.network;
import androidx.annotation.NonNull;
import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiMatic {
private static String TAG = ServerApiMatic.class.getSimpleName();
/**
* HTTP
* Infura
* <p>
* eth_getBalance
* eth_getTransactionCount
* eth_call
* eth_sendRawTransaction
* eth_gasPrice
*/
public static final String MATIC_ETH_GET_BALANCE = "eth_getBalance";
public static final String MATIC_ETH_GET_TRANSACTION_COUNT = "eth_getTransactionCount";
public static final String MATIC_ETH_GET_PENDING_COUNT = "eth_getPendingCount";
public static final String MATIC_ETH_CALL = "eth_call";
public static final String MATIC_ETH_SEND_RAW_TRANSACTION = "eth_sendRawTransaction";
public static final String MATIC_ETH_GAS_PRICE = "eth_gasPrice";
private int requestsCount=0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, int id, String wallet, String contract, String tx) {
requestsCount++;
MaticApi maticApi = App.Companion.getNetworkComponent().getRetrofitMaticTesnet().create(MaticApi.class);
InfuraBody infuraBody;
switch (method) {
case MATIC_ETH_GET_BALANCE:
case MATIC_ETH_GET_TRANSACTION_COUNT:
infuraBody = new InfuraBody(method, new String[]{wallet, "latest"}, id);
break;
case MATIC_ETH_GET_PENDING_COUNT:
infuraBody = new InfuraBody(MATIC_ETH_GET_TRANSACTION_COUNT, new String[]{wallet, "pending"}, id);
break;
case MATIC_ETH_CALL:
String address = wallet.substring(2);
infuraBody = new InfuraBody(method, new Object[]{new InfuraBody.EthCallParams("0x70a08231000000000000000000000000" + address, contract), "latest"}, id);
break;
case MATIC_ETH_SEND_RAW_TRANSACTION:
infuraBody = new InfuraBody(method, new String[]{tx}, id);
break;
case MATIC_ETH_GAS_PRICE:
infuraBody = new InfuraBody(method, id);
break;
default:
infuraBody = new InfuraBody();
}
Call<InfuraResponse> call = maticApi.matic(infuraBody);
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -0,0 +1,117 @@
package com.tangem.data.network;
import android.util.Log;
import com.tangem.data.network.model.RippleBody;
import com.tangem.data.network.model.RippleResponse;
import java.util.HashMap;
import androidx.annotation.NonNull;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ServerApiRipple {
private static String TAG = ServerApiRipple.class.getSimpleName();
public static final String RIPPLE_ACCOUNT_INFO = "account_info";
public static final String RIPPLE_ACCOUNT_UNCONFIRMED = "account_unconfirmed";
public static final String RIPPLE_SUBMIT = "submit";
public static final String RIPPLE_FEE = "fee";
public static final String RIPPLE_SERVER_STATE = "server_state";
private int requestsCount = 0;
public static String lastNode;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, RippleResponse rippleResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, String wallet, String tx) {
requestsCount++;
String rippleURL = "https://s1.ripple.com:51234"; //TODO: make random selection
lastNode = rippleURL; //TODO: show node instead of URL
Retrofit retrofitRipple = new Retrofit.Builder()
.baseUrl(rippleURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
RippleApi rippleApi = retrofitRipple.create(RippleApi.class);
RippleBody rippleBody;
HashMap<String, String> paramsMap;
switch (method) {
case RIPPLE_ACCOUNT_INFO:
paramsMap = new HashMap<>();
paramsMap.put("account", wallet);
paramsMap.put("ledger_index", "validated");
rippleBody = new RippleBody(method, paramsMap);
break;
case RIPPLE_ACCOUNT_UNCONFIRMED:
paramsMap = new HashMap<>();
paramsMap.put("account", wallet);
paramsMap.put("ledger_index", "current");
// paramsMap.put("queue", "true"); TODO: make queue check if needed
rippleBody = new RippleBody(RIPPLE_ACCOUNT_INFO, paramsMap);
break;
case RIPPLE_SERVER_STATE:
rippleBody = new RippleBody(method, new HashMap<>());
break;
case RIPPLE_FEE:
rippleBody = new RippleBody(method, new HashMap<>());
break;
case RIPPLE_SUBMIT:
paramsMap = new HashMap<>();
paramsMap.put("tx_blob", tx);
rippleBody = new RippleBody(method, paramsMap);
break;
default:
rippleBody = new RippleBody();
}
Call<RippleResponse> call = rippleApi.ripple(rippleBody);
call.enqueue(new Callback<RippleResponse>() {
@Override
public void onResponse(@NonNull Call<RippleResponse> call, @NonNull Response<RippleResponse> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<RippleResponse> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -0,0 +1,105 @@
package com.tangem.data.network;
import androidx.annotation.NonNull;
import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.model.InfuraBody;
import com.tangem.data.network.model.InfuraResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class ServerApiRootstock {
private static String TAG = ServerApiRootstock.class.getSimpleName();
/**
* HTTP
* Rootstock
* <p>
* eth_getBalance
* eth_getTransactionCount
* eth_call
* eth_sendRawTransaction
* eth_gasPrice
*/
public static final String ROOTSTOCK_ETH_GET_BALANCE = "eth_getBalance";
public static final String ROOTSTOCK_ETH_GET_TRANSACTION_COUNT = "eth_getTransactionCount";
public static final String ROOTSTOCK_ETH_GET_PENDING_COUNT = "eth_getPendingCount";
public static final String ROOTSTOCK_ETH_CALL = "eth_call";
public static final String ROOTSTOCK_ETH_SEND_RAW_TRANSACTION = "eth_sendRawTransaction";
public static final String ROOTSTOCK_ETH_GAS_PRICE = "eth_gasPrice";
private int requestsCount=0;
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
return requestsCount <= 0;
}
private ResponseListener responseListener;
public interface ResponseListener {
void onSuccess(String method, InfuraResponse infuraResponse);
void onFail(String method, String message);
}
public void setResponseListener(ResponseListener listener) {
responseListener = listener;
}
public void requestData(String method, int id, String wallet, String contract, String tx) {
requestsCount++;
RootstockApi rootstockApi = App.Companion.getNetworkComponent().getRetrofitRootstock().create(RootstockApi.class);
InfuraBody infuraBody;
switch (method) {
case ROOTSTOCK_ETH_GET_BALANCE:
case ROOTSTOCK_ETH_GET_TRANSACTION_COUNT:
infuraBody = new InfuraBody(method, new String[]{wallet, "latest"}, id);
break;
case ROOTSTOCK_ETH_GET_PENDING_COUNT:
infuraBody = new InfuraBody(ROOTSTOCK_ETH_GET_TRANSACTION_COUNT, new String[]{wallet, "pending"}, id);
break;
case ROOTSTOCK_ETH_CALL:
String address = wallet.substring(2);
infuraBody = new InfuraBody(method, new Object[]{new InfuraBody.EthCallParams("0x70a08231000000000000000000000000" + address, contract), "latest"}, id);
break;
case ROOTSTOCK_ETH_SEND_RAW_TRANSACTION:
infuraBody = new InfuraBody(method, new String[]{tx}, id);
break;
case ROOTSTOCK_ETH_GAS_PRICE:
infuraBody = new InfuraBody(method, id);
break;
default:
infuraBody = new InfuraBody();
}
Call<InfuraResponse> call = rootstockApi.rootstock(infuraBody);
call.enqueue(new Callback<InfuraResponse>() {
@Override
public void onResponse(@NonNull Call<InfuraResponse> call, @NonNull Response<InfuraResponse> response) {
if (response.code() == 200) {
requestsCount--;
responseListener.onSuccess(method, response.body());
Log.i(TAG, "requestData " + method + " onResponse " + response.code());
} else {
responseListener.onFail(method, String.valueOf(response.code()));
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
}
}
@Override
public void onFailure(@NonNull Call<InfuraResponse> call, @NonNull Throwable t) {
responseListener.onFail(method, String.valueOf(t.getMessage()));
Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
}
});
}
}

View file

@ -2,9 +2,14 @@ package com.tangem.data.network;
class ServerURL {
static final String API_TANGEM = "https://verify.tangem.com/";
static final String API_COINMARKETCAP = "https://api.coinmarketcap.com/";
static final String API_INFURA = "https://mainnet.infura.io/v3/";
static final String API_COINMARKETCAP = "https://pro-api.coinmarketcap.com/";
static final String API_INFURA = "https://mainnet.infura.io/";
static final String API_ESTIMATEFEE = "https://estimatefee.com/";
static final String API_UPDATE_VERSION = "https://raw.githubusercontent.com/";
static final String API_ROOTSTOCK = "https://public-node.rsk.co/";
static final String API_BLOCKCYPHER = "https://api.blockcypher.com/";
static final String API_BINANCE = "https://dex.binance.org/";
static final String API_BINANCE_TESTNET = "https://testnet-dex.binance.org/";
static final String API_MATIC_TESTNET = "https://testnet2.matic.network";
static final String API_STELLAR = "https://horizon-testnet.stellar.org";
}

View file

@ -0,0 +1,9 @@
package com.tangem.data.network.model;
public class AdaliteBody {
private String signedTx;
public AdaliteBody(String signedTx) {
this.signedTx = signedTx;
}
}

View file

@ -0,0 +1,45 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class AdaliteResponse(
@SerializedName("Right")
var right: AdaliteAddressData? = null
)
data class AdaliteResponseUtxo(
@SerializedName("Right")
var right: List<AdaliteUtxoData>
)
data class AdaliteAddressData(
@SerializedName("caAddress")
var caAddress: String? = null,
@SerializedName("caBalance")
var caBalance: AdaliteCoins? = null,
@SerializedName("caTxList")
var caTxList: List<AdaliteTxData>
)
data class AdaliteCoins(
@SerializedName("getCoin")
var getCoin: Long? = null
)
data class AdaliteUtxoData(
@SerializedName("cuId")
var cuId: String? = null,
@SerializedName("cuOutIndex")
var cuOutIndex: Int? = null,
@SerializedName("cuCoins")
var cuCoins: AdaliteCoins? = null
)
data class AdaliteTxData(
@SerializedName("ctbId")
var ctbId: String? = null
)

View file

@ -0,0 +1,16 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class BinanceFees(
@SerializedName("fixed_fee_params")
var fixed_fee_params: BinanceFixedFee? = null
)
data class BinanceFixedFee(
@SerializedName("msg_type")
var msg_type: String? = null,
@SerializedName("fee")
var fee: Int? = null
)

View file

@ -0,0 +1,9 @@
package com.tangem.data.network.model;
public class BlockcypherBody {
private String tx;
public BlockcypherBody(String tx) {
this.tx = tx;
}
}

View file

@ -0,0 +1,45 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class BlockcypherResponse(
@SerializedName("address")
var address: String? = null,
@SerializedName("balance")
var balance: Long? = null,
@SerializedName("unconfirmed_balance")
var unconfirmed_balance: Long? = null,
@SerializedName("txrefs")
var txrefs: List<BlockcypherTxref>? = null
)
data class BlockcypherTxref(
@SerializedName("tx_hash")
var tx_hash: String? = null,
@SerializedName("tx_output_n")
var tx_output_n: Int? = null,
@SerializedName("value")
var value: Long? = null,
@SerializedName("confirmations")
var confirmations: Long? = null,
@SerializedName("script")
var script: String? = null
)
data class BlockcypherFee(
@SerializedName("low_fee_per_kb")
var low_fee_per_kb: Long? = null,
@SerializedName("medium_fee_per_kb")
var medium_fee_per_kb: Long? = null,
@SerializedName("high_fee_per_kb")
var high_fee_per_kb: Long? = null
)

View file

@ -4,6 +4,7 @@ public class InfuraBody {
private String method;
private Object[] params;
private int id;
private String jsonrpc = "2.0";
public InfuraBody() {
}

View file

@ -10,8 +10,8 @@ data class InfuraResponse(
var id: Int? = null,
@SerializedName("result")
var result: String = "",
var result: String? = null,
@SerializedName("error")
var error: String = ""
var error: Object
)

View file

@ -0,0 +1,9 @@
package com.tangem.data.network.model;
public class InsightBody {
private String rawtx;
public InsightBody(String rawtx){
this.rawtx = rawtx;
}
}

View file

@ -0,0 +1,38 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class InsightResponse(
@SerializedName("balanceSat")
var balanceSat: Long? = null,
@SerializedName("unconfirmedBalanceSat")
var unconfirmedBalanceSat: Long? = null,
@SerializedName("addrStr")
var addrStr: String = "",
@SerializedName("txid")
var txid: String = "",
@SerializedName("satoshis")
var satoshis: Long? = null,
@SerializedName("height")
var height: Int? = null,
@SerializedName("2")
var fee2: String = "",
@SerializedName("3")
var fee3: String = "",
@SerializedName("6")
var fee6: String = "",
@SerializedName("rawtx")
var rawtx: String = "",
@SerializedName("error")
var error: String = ""
)

View file

@ -1,76 +1,23 @@
package com.tangem.data.network.model
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize
@Parcelize
data class RateInfoResponse(
@SerializedName(ID)
var id: String = "",
@SerializedName("data")
var data: RateData? = null
)
@SerializedName(NAME)
var name: String = "",
data class RateData(
@SerializedName("quote")
var quote: Quote? = null
)
@SerializedName(SYMBOL)
var symbol: String = "",
data class Quote(
@SerializedName("USD")
var usd: CurrencyRate? = null
)
@SerializedName(RANK)
var rank: String = "",
@SerializedName(PRICE_USD)
var priceUsd: String = "",
@SerializedName(PRICE_BTC)
var priceBtc: String = "",
@SerializedName(VOLUME_USD_24H)
var volumeUsd24h: String = "",
@SerializedName(MARKET_CAP_USD)
var marketCapUsd: String = "",
@SerializedName(AVAILABLE_SUPPLY)
var availableSupply: String = "",
@SerializedName(TOTAL_SUPPLY)
var totalSupply: String = "",
@SerializedName(MAX_SUPPLY)
var maxSupply: String = "",
@SerializedName(PERCENT_CHANGE_1H)
var percentChange1h: String = "",
@SerializedName(PERCENT_CHANGE_24H)
var percentChange24h: String = "",
@SerializedName(PERCENT_CHANGE_7H)
var percentChange7h: String = "",
@SerializedName(LAST_UPDATED)
var lastUpdated: String = ""
) : Parcelable {
companion object {
val TAG: String = RateInfoResponse::class.java.simpleName
const val ID = "id"
const val NAME = "name"
const val SYMBOL = "symbol"
const val RANK = "rank"
const val PRICE_USD = "price_usd"
const val PRICE_BTC = "price_btc"
const val VOLUME_USD_24H = "24h_volume_usd"
const val MARKET_CAP_USD = "market_cap_usd"
const val AVAILABLE_SUPPLY = "available_supply"
const val TOTAL_SUPPLY = "total_supply"
const val MAX_SUPPLY = "max_supply"
const val PERCENT_CHANGE_1H = "percent_change_1h"
const val PERCENT_CHANGE_24H = "percent_change_24h"
const val PERCENT_CHANGE_7H = "percent_change_7d"
const val LAST_UPDATED = "last_updated"
}
}
data class CurrencyRate(
@SerializedName("price")
var price: Float? = null
)

View file

@ -0,0 +1,24 @@
package com.tangem.data.network.model;
import java.util.ArrayList;
import java.util.HashMap;
public class RippleBody {
private String method;
private ArrayList<HashMap<String,String>> params;
public RippleBody() {
}
//for RIPPLE_FEE
public RippleBody(String method) {
this.method = method;
}
public RippleBody(String method, HashMap<String,String> paramsMap) {
this.method = method;
ArrayList<HashMap<String, String>> paramsList = new ArrayList<>();
paramsList.add(paramsMap);
this.params = paramsList;
}
}

View file

@ -0,0 +1,78 @@
package com.tangem.data.network.model
import com.google.gson.annotations.SerializedName
data class RippleResponse(
@SerializedName("result")
var result: RippleResult? = null
)
data class RippleResult(
@SerializedName("account_data")
var account_data: RippleAccountData? = null,
@SerializedName("validated")
var validated: Boolean? = null,
//for RIPPLE_FEE
@SerializedName("drops")
var drops: RippleFeeDrops? = null,
//for RIPPLE_SUBMIT
@SerializedName("engine_result_code")
var engine_result_code: Int? = null,
//for RIPPLE_SUBMIT
@SerializedName ("engine_result_message")
var engine_result_message: String? = null,
//for RIPPLE_SUBMIT
@SerializedName("error")
var error: String? = null,
//for RIPPLE_SUBMIT
@SerializedName("error_exception")
var error_exception: String? = null,
//for RIPPLE_SERVER_STATE
@SerializedName("state")
var state: RippleState? = null,
//for "Account not found error"
@SerializedName("error_code")
var error_code: Int? = null
)
data class RippleAccountData(
@SerializedName("Account")
var account: String? = null,
@SerializedName("Balance")
var balance: String? = null,
@SerializedName("Sequence")
var sequence: Long? = null
)
data class RippleFeeDrops(
//enough to put tx to queue
@SerializedName("minimum_fee")
var minimum_fee: String? = null,
//enough to put tx to current ledger
@SerializedName("open_ledger_fee")
var open_ledger_fee: String? = null,
@SerializedName("median_fee")
var median_fee: String? = null
)
data class RippleState(
@SerializedName("validated_ledger")
var validated_ledger: RippleLedger? = null
)
data class RippleLedger(
@SerializedName("reserve_base")
var reserve_base: Long? = null
)

View file

@ -1,21 +0,0 @@
package com.tangem.di;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import javax.inject.Singleton;
@Singleton
public class AppModule {
private Context appContext;
public AppModule(@NotNull Context context) {
appContext = context;
}
Context provideContext() {
return appContext;
}
}

View file

@ -0,0 +1,18 @@
package com.tangem.di
import android.app.Application
import android.content.Context
import dagger.Module
import javax.inject.Singleton
import dagger.Provides
@Module
class AppModule {
@Provides
@Singleton
fun provideContext(application: Application): Context {
return application
}
}

View file

@ -3,11 +3,17 @@ package com.tangem.di
import android.app.Activity
import android.nfc.Tag
import com.tangem.Constant
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.activity.*
import com.tangem.wallet.TangemContext
import com.tangem.ui.PrepareTransactionActivity
import com.tangem.ui.activity.*
import com.tangem.wallet.BuildConfig
class Navigator {
fun showSettings(context: Activity) {
context.startActivity(SettingsActivity.callingIntent(context))
}
fun showMain(context: Activity) {
context.startActivity(MainActivity.callingIntent(context))
}
@ -68,8 +74,15 @@ class Navigator {
context.startActivityForResult(PurgeActivity.callingIntent(context, ctx), Constant.REQUEST_CODE_PURGE)
}
fun showPreparePayment(context: Activity, ctx: TangemContext) {
context.startActivityForResult(PreparePaymentActivity.callingIntent(context, ctx), Constant.REQUEST_CODE_SEND_PAYMENT)
fun showPrepareTransaction(context: Activity, ctx: TangemContext) {
when (BuildConfig.FLAVOR) {
Constant.FLAVOR_TANGEM_CARDANO -> {
context.startActivity(PrepareTransactionActivity.callingIntent(context, ctx))
}
else -> {
context.startActivityForResult(PrepareTransactionActivity.callingIntent(context, ctx), Constant.REQUEST_CODE_SEND_TRANSACTION)
}
}
}
fun showCreateNewWallet(context: Activity, ctx: TangemContext) {

View file

@ -1,38 +0,0 @@
package com.tangem.di;
import com.tangem.presentation.activity.EmptyWalletActivity;
import com.tangem.presentation.activity.LoadedWalletActivity;
import com.tangem.presentation.activity.LogoActivity;
import com.tangem.presentation.activity.MainActivity;
import com.tangem.presentation.activity.PrepareCryptonitOtherApiWithdrawalActivity;
import com.tangem.presentation.activity.PrepareKrakenWithdrawalActivity;
import com.tangem.presentation.activity.PreparePaymentActivity;
import com.tangem.presentation.activity.VerifyCardActivity;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
// AppModule.class,
NavigatorModule.class})
public interface NavigatorComponent {
void inject(LogoActivity activity);
void inject(MainActivity activity);
void inject(PreparePaymentActivity activity);
void inject(PrepareCryptonitOtherApiWithdrawalActivity activity);
void inject(PrepareKrakenWithdrawalActivity activity);
void inject(LoadedWalletActivity activity);
void inject(VerifyCardActivity activity);
void inject(EmptyWalletActivity activity);
}

View file

@ -0,0 +1,39 @@
package com.tangem.di
import com.tangem.ui.activity.EmptyWalletActivity
import com.tangem.ui.activity.LoadedWalletActivity
import com.tangem.ui.activity.LogoActivity
import com.tangem.ui.activity.MainActivity
import com.tangem.ui.activity.PrepareCryptonitOtherApiWithdrawalActivity
import com.tangem.ui.activity.PrepareKrakenWithdrawalActivity
import com.tangem.ui.PrepareTransactionActivity
import com.tangem.ui.activity.PurgeActivity
import com.tangem.ui.activity.VerifyCardActivity
import javax.inject.Singleton
import dagger.Component
@Singleton
@Component(modules = [NavigatorModule::class])
interface NavigatorComponent {
fun inject(activity: LogoActivity)
fun inject(activity: MainActivity)
fun inject(activity: PurgeActivity)
fun inject(activity: PrepareTransactionActivity)
fun inject(activity: PrepareCryptonitOtherApiWithdrawalActivity)
fun inject(activity: PrepareKrakenWithdrawalActivity)
fun inject(activity: LoadedWalletActivity)
fun inject(activity: VerifyCardActivity)
fun inject(activity: EmptyWalletActivity)
}

View file

@ -1,17 +0,0 @@
package com.tangem.di;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
class NavigatorModule {
@Singleton
@Provides
Navigator provideNavigator() {
return new Navigator();
}
}

View file

@ -0,0 +1,31 @@
package com.tangem.di
import com.tangem.ui.dialog.WaitSecurityDelayDialogNew
import javax.inject.Singleton
import dagger.Module
import dagger.Provides
@Module
internal class NavigatorModule {
@Singleton
@Provides
fun provideNavigator(): Navigator {
return Navigator()
}
@Singleton
@Provides
fun provideToastHelper(): ToastHelper {
return ToastHelper()
}
@Singleton
@Provides
fun provideWaitSecurityDelayDialogNew(): WaitSecurityDelayDialogNew {
return WaitSecurityDelayDialogNew()
}
}

View file

@ -1,32 +0,0 @@
package com.tangem.di;
import com.tangem.data.network.Server;
import java.net.Socket;
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Component;
import retrofit2.Retrofit;
@Singleton
@Component(modules = {NetworkModule.class})
public interface NetworkComponent {
@Named(Server.ApiInfura.URL_INFURA)
Retrofit getRetrofitInfura();
@Named(Server.ApiEstimatefee.URL_ESTIMATEFEE)
Retrofit getRetrofitEstimatefee();
@Named(Server.ApiCoinmarket.URL_COINMARKET)
Retrofit getRetrofitCoinmarketcap();
@Named(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
Retrofit getRetrofitGithubusercontent();
@Named("socket")
Socket getSocket();
}

View file

@ -0,0 +1,41 @@
package com.tangem.di
import com.tangem.data.network.Server
import java.net.Socket
import javax.inject.Named
import javax.inject.Singleton
import dagger.Component
import retrofit2.Retrofit
@Singleton
@Component(modules = [NetworkModule::class])
interface NetworkComponent {
@get:Named(Server.ApiInfura.URL_INFURA)
val retrofitInfura: Retrofit
@get:Named(Server.ApiMaticTesnet.URL_MATIC_TESTNET)
val retrofitMaticTesnet: Retrofit
@get:Named(Server.ApiEstimatefee.URL_ESTIMATEFEE)
val retrofitEstimatefee: Retrofit
@get:Named(Server.ApiCoinmarket.URL_COINMARKET)
val retrofitCoinmarketcap: Retrofit
@get:Named(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
val retrofitGitHubUserContent: Retrofit
@get:Named(Server.ApiRootstock.URL_ROOTSTOCK)
val retrofitRootstock: Retrofit
@get:Named(Server.ApiBlockcypher.URL_BLOCKCYPHER)
val retrofitBlockcypher: Retrofit
@get:Named("socket")
val socket: Socket
}

View file

@ -1,96 +0,0 @@
package com.tangem.di;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.tangem.data.network.Server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@Module
class NetworkModule {
@Singleton
@Provides
@Named(Server.ApiInfura.URL_INFURA)
Retrofit provideRetrofitInfura() {
return new Retrofit.Builder()
.baseUrl(Server.ApiInfura.URL_INFURA)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
@Singleton
@Provides
@Named(Server.ApiEstimatefee.URL_ESTIMATEFEE)
Retrofit provideRetrofitEstimatefee() {
return new Retrofit.Builder()
.baseUrl(Server.ApiEstimatefee.URL_ESTIMATEFEE)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
@Singleton
@Provides
@Named(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
Retrofit provideGithubusercontent() {
return new Retrofit.Builder()
.baseUrl(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
.build();
}
@Singleton
@Provides
@Named(Server.ApiCoinmarket.URL_COINMARKET)
Retrofit provideRetrofitCoinmarketcap() {
return new Retrofit.Builder()
.baseUrl(Server.ApiCoinmarket.URL_COINMARKET)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
private OkHttpClient createOkHttpClient() {
return new OkHttpClient.Builder().
addInterceptor(createHttpLoggingInterceptor()).
build();
}
private HttpLoggingInterceptor createHttpLoggingInterceptor() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
return logging;
}
@Provides
@Named("socket")
Socket provideSocket() {
Socket socket = new Socket();
try {
socket.setSoTimeout(2000);
try {
socket.bind(new InetSocketAddress(0));
} catch (IOException e) {
e.printStackTrace();
}
} catch (SocketException e) {
e.printStackTrace();
}
return socket;
}
}

View file

@ -0,0 +1,141 @@
package com.tangem.di
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import com.tangem.data.network.Server
import com.tangem.wallet.BuildConfig
import java.io.IOException
import java.net.InetSocketAddress
import java.net.Socket
import java.net.SocketException
import javax.inject.Named
import javax.inject.Singleton
import dagger.Module
import dagger.Provides
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
@Module
internal class NetworkModule {
@Singleton
@Provides
@Named(Server.ApiInfura.URL_INFURA)
fun provideRetrofitInfura(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiInfura.URL_INFURA)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiRootstock.URL_ROOTSTOCK)
fun provideRetrofitRootstock(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiRootstock.URL_ROOTSTOCK)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiMaticTesnet.URL_MATIC_TESTNET)
fun provideRetrofitMaticTestnet(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiMaticTesnet.URL_MATIC_TESTNET)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiEstimatefee.URL_ESTIMATEFEE)
fun provideRetrofitEstimatefee(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiEstimatefee.URL_ESTIMATEFEE)
.addConverterFactory(GsonConverterFactory.create())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
fun provideGithubusercontent(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiUpdateVersion.URL_UPDATE_VERSION)
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiCoinmarket.URL_COINMARKET)
fun provideRetrofitCoinmarketcap(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiCoinmarket.URL_COINMARKET)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
// if (BuildConfig.DEBUG)
// builder.client(createOkHttpClient())
return builder.build()
}
@Singleton
@Provides
@Named(Server.ApiBlockcypher.URL_BLOCKCYPHER)
fun provideRetrofitBlockcypher(): Retrofit {
val builder = Retrofit.Builder()
.baseUrl(Server.ApiBlockcypher.URL_BLOCKCYPHER)
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
if (BuildConfig.DEBUG)
builder.client(createOkHttpClient())
return builder.build()
}
private fun createOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(createHttpLoggingInterceptor()).build()
}
private fun createHttpLoggingInterceptor(): HttpLoggingInterceptor {
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
return logging
}
@Provides
@Named("socket")
fun provideSocket(): Socket {
val socket = Socket()
try {
socket.soTimeout = 2000
try {
socket.bind(InetSocketAddress(0))
} catch (e: IOException) {
e.printStackTrace()
}
} catch (e: SocketException) {
e.printStackTrace()
}
return socket
}
}

View file

@ -0,0 +1,70 @@
package com.tangem.di
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.view.ViewGroup
import android.widget.Toast
import com.google.android.material.snackbar.Snackbar
import com.tangem.Constant
import com.tangem.wallet.R
import java.util.*
class ToastHelper {
fun showSnackbarUpdateVersion(context: Context, vg: ViewGroup, versionName: String) {
Snackbar.make(vg, String.format(context.getString(R.string.new_app_version), versionName), Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.update) {
try {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(Constant.URL_TANGEM)
context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
}
}.show()
}
fun showSnackbarSuccess(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_INDEFINITE)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_okay))
snackbar.setAction(R.string.ok) {
snackbar.dismiss()
}
snackbar.show()
}
fun showSnackbarError(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_INDEFINITE)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_err))
snackbar.setAction(R.string.ok) {
snackbar.dismiss()
}
snackbar.show()
}
fun showSnackbarWarning(context: Context, vg: ViewGroup, message: String) {
val snackbar = Snackbar.make(vg, message, Snackbar.LENGTH_LONG)
val snackView = snackbar.view
snackView.setBackgroundColor(context.resources.getColor(R.color.msg_err))
snackbar.show()
}
private var singleToast: Toast? = null
private var showTime: Date = Date()
fun showSingleToast(context: Context?, text: String) {
if (singleToast == null || !singleToast!!.view.isShown || showTime.time + 2000 < Date().time) {
if (singleToast != null)
singleToast!!.cancel()
if (context != null) {
singleToast = Toast.makeText(context, text, Toast.LENGTH_LONG)
singleToast!!.show()
showTime = Date()
}
}
}
}

View file

@ -0,0 +1,25 @@
package com.tangem.di
import com.tangem.ui.ConfirmTransactionActivity
import com.tangem.ui.PrepareTransactionActivity
import com.tangem.ui.activity.EmptyWalletActivity
import com.tangem.ui.activity.LoadedWalletActivity
import com.tangem.ui.activity.MainActivity
import dagger.Component
import javax.inject.Singleton
@Singleton
@Component(modules = [NavigatorModule::class])
interface ToastHelperComponent {
fun inject(activity: MainActivity)
fun inject(activity: LoadedWalletActivity)
fun inject(activity: ConfirmTransactionActivity)
fun inject(activity: PrepareTransactionActivity)
fun inject(activity: EmptyWalletActivity)
}

View file

@ -1,61 +0,0 @@
package com.tangem.domain.wallet
import android.util.Log
import com.tangem.domain.wallet.btc.BtcEngine
import com.tangem.domain.wallet.eth.EthEngine
import com.tangem.domain.wallet.token.TokenEngine
import com.tangem.domain.wallet.bch.BtcCashEngine
import com.tangem.data.Blockchain
import com.tangem.domain.wallet.ltc.LtcEngine
import com.tangem.domain.wallet.xlm.XlmEngine
/**
* Factory for create specific engine
*
* @param Blockchain
* @param TangemContext
*
*/
object CoinEngineFactory {
private val TAG = CoinEngineFactory::class.java.simpleName
fun create(blockchain: Blockchain): CoinEngine? {
return when (blockchain) {
Blockchain.Bitcoin, Blockchain.BitcoinTestNet -> BtcEngine()
Blockchain.BitcoinCash -> BtcCashEngine()
Blockchain.Ethereum, Blockchain.EthereumTestNet -> EthEngine()
Blockchain.Token -> TokenEngine()
Blockchain.Litecoin -> LtcEngine()
Blockchain.StellarTestNet, Blockchain.Stellar -> XlmEngine()
else -> null
}
}
fun create(context: TangemContext): CoinEngine? {
var result: CoinEngine?
try {
result = if (Blockchain.BitcoinCash == context.blockchain)
BtcCashEngine(context)
else if (Blockchain.Bitcoin == context.blockchain || Blockchain.BitcoinTestNet == context.blockchain)
BtcEngine(context)
else if (Blockchain.Ethereum == context.blockchain || Blockchain.EthereumTestNet == context.blockchain)
EthEngine(context)
else if (Blockchain.Token == context.blockchain)
TokenEngine(context)
else if (Blockchain.Litecoin == context.blockchain)
LtcEngine(context)
else if (Blockchain.Stellar == context.blockchain || Blockchain.StellarTestNet == context.blockchain)
XlmEngine(context)
else
return null
} catch (e: Exception) {
e.printStackTrace()
result = null
Log.e(TAG, "Can't create CoinEngine!")
}
return result
}
}

View file

@ -1,24 +0,0 @@
package com.tangem.domain.wallet.bch
enum class BitcoinCashNode(val host: String, val port: Int, val proto: String) {
N_001("electrumx.hillsideinternet.com", 50002, "ssl"),
N_002("bch0.kister.net", 50002, "ssl"),
N_003("abc1.hsmiths.com", 60002, "ssl"),
N_004("bch.curalle.ovh", 50002, "ssl"),
N_005("207.180.215.112", 52002, "ssl"),
N_006("bch.imaginary.cash", 50002, "ssl"),
N_007("dedi.jochen-hoenicke.de", 51002, "ssl"),
N_008("crypto.mldlabs.com", 50002, "ssl"),
N_009("bch.electrumx.cash", 50002, "ssl"),
N_010("electroncash.cascharia.com", 50002, "ssl"),
N_011("bch.crypto.mldlabs.com", 50002, "ssl"),
N_012("electron-cash.dragon.zone", 50002, "ssl"),
N_013("electron.coinucopia.io", 50002, "ssl"),
N_014("blackie.c3-soft.com", 50002, "ssl"),
N_015("electroncash.ueo.ch", 51002, "ssl"),
N_016("electrum.imaginary.cash", 50002, "ssl"),
N_017("35.157.238.5", 51002, "ssl"),
N_018("bitcoincash.quangld.com", 50002, "ssl"),
N_019("bch.stitthappens.com", 50002, "ssl"),
N_020("electroncash.dk", 50002, "ssl"),
}

View file

@ -1,85 +0,0 @@
package com.tangem.domain.wallet.btc
enum class BitcoinNode(val host: String, val port: Int, val proto: String) {
N_001("electrum.anduck.net", 50001, "tcp"),
N_002("ip119.ip-54-37-91.eu", 50001, "tcp"),
N_003("electrum.qtornado.com", 50001, "tcp"),
N_004("ip239.ip-54-36-234.eu", 50001, "tcp"),
N_005("electrum-server.ninja", 50001, "tcp"),
N_006("174.138.11.174", 50001, "tcp"),
N_007("ndnd.selfhost.eu", 50001, "tcp"),
N_008("btc.cihar.com", 50001, "tcp"),
N_009("vps.hsmiths.com", 8080, "tcp"),
N_010("electrum.hsmiths.com", 8080, "tcp"),
N_011("ip120.ip-54-37-91.eu", 50001, "tcp"),
N_012("vps.hsmiths.com", 50001, "tcp"),
N_013("orannis.com", 50001, "tcp"),
N_014("ip101.ip-54-37-91.eu", 50001, "tcp"),
N_015("e-x.not.fyi", 50001, "tcp"),
N_016("electrum.hsmiths.com", 50001, "tcp"),
N_017("electrum.vom-stausee.de", 50001, "tcp"),
N_018("bitcoin.corgi.party", 50001, "tcp"),
N_019("electrum2.eff.ro", 50001, "tcp"),
N_020("electrum.coinucopia.io", 50001, "tcp"),
N_021("electrum.eff.ro", 50001, "tcp"),
N_022("btc.xskyx.net", 50001, "tcp"),
N_023("kirsche.emzy.de", 50001, "tcp"),
N_024("electrum.petrkr.net", 50001, "tcp"),
N_025("btc.knas.systems", 50001, "tcp"),
N_026("b.ooze.cc", 50002, "ssl"),
N_027("electrum.nute.net", 50002, "ssl"),
N_028("ndnd.selfhost.eu", 50002, "ssl"),
N_029("electrum.coinop.cc", 50002, "ssl"),
N_030("orannis.com", 50002, "ssl"),
N_031("electrum.vom-stausee.de", 50002, "ssl"),
N_032("ip119.ip-54-37-91.eu", 50002, "ssl"),
N_033("ip101.ip-54-37-91.eu", 50002, "ssl"),
N_034("electrum2.villocq.com", 50002, "ssl"),
N_035("dedi.jochen-hoenicke.de", 50002, "ssl"),
N_036("174.138.11.174", 50002, "ssl"),
N_037("tomscryptos.com", 50002, "ssl"),
N_038("elec.luggs.co", 443, "ssl"),
N_039("ip239.ip-54-36-234.eu", 50002, "ssl"),
N_040("bitcoins.sk", 50002, "ssl"),
N_041("btc.cihar.com", 50002, "ssl"),
N_042("e-x.not.fyi", 50002, "ssl"),
N_043("ip120.ip-54-37-91.eu", 50002, "ssl"),
N_044("electrum.villocq.com", 50002, "ssl"),
N_045("electrum.anduck.net", 50012, "ssl"),
N_046("technetium.network", 50002, "ssl"),
N_047("electrum.coinucopia.io", 50002, "ssl"),
N_048("currentlane.lovebitco.in", 50002, "ssl"),
N_049("dimon.trimon.de", 50002, "ssl"),
N_050("rbx.curalle.ovh", 50002, "ssl"),
N_051("btc.gravitech.net", 50002, "ssl"),
N_052("hetzner01.fischl-online.de", 50002, "ssl"),
N_053("fn.48.org", 50002, "ssl"),
N_054("185.64.116.15", 50002, "ssl"),
N_055("kirsche.emzy.de", 50002, "ssl"),
N_056("109.192.105.174", 50002, "ssl"),
N_057("fedaykin.goip.de", 50002, "ssl"),
N_058("vps.hsmiths.com", 50002, "ssl"),
N_059("104.250.141.242", 50002, "ssl"),
N_060("electrum.qtornado.com", 50002, "ssl"),
N_061("electrum-server.ninja", 50002, "ssl"),
N_062("electrum2.eff.ro", 50002, "ssl"),
N_063("electrum.hsmiths.com", 995, "ssl"),
N_064("electrum.hsmiths.com", 50002, "ssl"),
N_065("139.162.14.142", 50002, "ssl"),
N_066("electrum.eff.ro", 50002, "ssl"),
N_067("electrum.taborsky.cz", 50002, "ssl"),
N_068("electrum.festivaldelhumor.org", 50002, "ssl"),
N_069("electrum.petrkr.net", 50002, "ssl"),
N_070("us.electrum.be", 50002, "ssl"),
N_071("bitcoin-node.org", 50002, "ssl"),
N_072("vmd27610.contaboserver.net", 50002, "ssl"),
N_073("electrumx.soon.it", 50002, "ssl"),
N_074("vmd30612.contaboserver.net", 50002, "ssl"),
N_075("enode.duckdns.org", 50002, "ssl"),
N_076("81-7-13-84.blue.kundencontroller.de", 50002, "ssl"),
N_077("electrum.scumm.it", 50002, "ssl"),
N_078("helicarrier.bauerj.eu", 50002, "ssl"),
N_079("tardis.bauerj.eu", 50002, "ssl"),
N_080("such.ninja", 50002, "ssl"),
N_081("electrum.be", 50002, "ssl"),
}

View file

@ -1,824 +0,0 @@
package com.tangem.domain.wallet.btc;
import android.net.Uri;
import android.text.InputFilter;
import android.util.Log;
import com.tangem.data.network.ServerApiCommon;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.domain.wallet.BalanceValidator;
import com.tangem.domain.wallet.Base58;
import com.tangem.data.Blockchain;
import com.tangem.domain.wallet.CoinData;
import com.tangem.domain.wallet.CoinEngine;
import com.tangem.tangemcard.data.TangemCard;
import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.Transaction;
import com.tangem.domain.wallet.UnspentOutputInfo;
import com.tangem.domain.wallet.BTCUtils;
import com.tangem.tangemcard.tasks.SignTask;
import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.util.DerEncodingUtil;
import com.tangem.tangemcard.util.Util;
import com.tangem.wallet.R;
import com.tangem.data.network.ElectrumRequest;
import com.tangem.data.network.ServerApiElectrum;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BtcEngine extends CoinEngine {
private static final String TAG = BtcEngine.class.getSimpleName();
public BtcData coinData = null;
public BtcEngine(TangemContext context) throws Exception {
super(context);
if (context.getCoinData() == null) {
coinData = new BtcData();
context.setCoinData(coinData);
} else if (context.getCoinData() instanceof BtcData) {
coinData = (BtcData) context.getCoinData();
} else {
throw new Exception("Invalid type of Blockchain data for BtcEngine");
}
}
public BtcEngine() {
super();
}
private static int getDecimals() {
return 8;
}
private void checkBlockchainDataExists() throws Exception {
if (coinData == null) throw new Exception("No blockchain data");
}
@Override
public boolean awaitingConfirmation() {
if (coinData == null) return false;
return coinData.getBalanceUnconfirmed() != 0;
}
@Override
public String getBalanceHTML() {
Amount balance = getBalance();
if (balance != null) {
return balance.toDescriptionString(getDecimals());
} else {
return "";
}
}
@Override
public String getBalanceCurrency() {
return "BTC";
}
@Override
public String getOfflineBalanceHTML() {
InternalAmount offlineInternalAmount = convertToInternalAmount(ctx.getCard().getOfflineBalance());
Amount offlineAmount = convertToAmount(offlineInternalAmount);
return offlineAmount.toDescriptionString(getDecimals());
}
@Override
public boolean isBalanceNotZero() {
if (coinData == null) return false;
if (coinData.getBalanceInInternalUnits() == null) return false;
return coinData.getBalanceInInternalUnits().notZero();
}
@Override
public boolean hasBalanceInfo() {
if (coinData == null) return false;
return coinData.hasBalanceInfo();
}
@Override
public boolean isExtractPossible() {
if (!hasBalanceInfo()) {
ctx.setMessage(R.string.cannot_obtain_data_from_blockchain);
} else if (!isBalanceNotZero()) {
ctx.setMessage(R.string.wallet_empty);
} else if (awaitingConfirmation()) {
ctx.setMessage(R.string.please_wait_while_previous);
} else if (coinData.getUnspentTransactions().size() == 0) {
ctx.setMessage(R.string.please_wait_for_confirmation);
} else {
return true;
}
return false;
}
@Override
public String getFeeCurrency() {
return "BTC";
}
@Override
public boolean validateAddress(String address) {
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 (ctx.getBlockchain() != Blockchain.BitcoinTestNet && ctx.getBlockchain() != Blockchain.Bitcoin) {
return false;
}
if (ctx.getBlockchain() == Blockchain.BitcoinTestNet && (address.startsWith("1") || address.startsWith("3"))) {
return false;
}
return true;
}
@Override
public boolean isNeedCheckNode() {
return true;
}
@Override
public Uri getShareWalletUriExplorer() {
return Uri.parse((ctx.getBlockchain() == Blockchain.Bitcoin ? "https://blockchain.info/address/" : "https://testnet.blockchain.info/address/") + ctx.getCoinData().getWallet());
}
@Override
public Uri getShareWalletUri() {
if (ctx.getCard().getDenomination() != null) {
return Uri.parse("bitcoin:" + ctx.getCoinData().getWallet() + "?amount=" + convertToAmount(convertToInternalAmount(ctx.getCard().getDenomination())).toValueString(8));
} else {
return Uri.parse("bitcoin:" + ctx.getCoinData().getWallet());
}
}
@Override
public InputFilter[] getAmountInputFilters() {
return new InputFilter[]{new DecimalDigitsInputFilter(getDecimals())};
}
@Override
public boolean checkNewTransactionAmount(Amount amount) {
if (coinData == null) return false;
if (amount.compareTo(convertToAmount(coinData.getBalanceInInternalUnits())) > 0) {
return false;
}
return true;
}
@Override
public boolean checkNewTransactionAmountAndFee(Amount amountValue, Amount feeValue, Boolean isIncludeFee) {
InternalAmount fee;
InternalAmount amount;
try {
checkBlockchainDataExists();
amount = convertToInternalAmount(amountValue);
fee = convertToInternalAmount(feeValue);
} catch (Exception e) {
e.printStackTrace();
return false;
}
if (fee == null || amount == null)
return false;
if (fee.isZero() || amount.isZero())
return false;
if (isIncludeFee && (amount.compareTo(coinData.getBalanceInInternalUnits()) > 0 || amount.compareTo(fee) < 0))
return false;
if (!isIncludeFee && amount.add(fee).compareTo(coinData.getBalanceInInternalUnits()) > 0)
return false;
return true;
}
@Override
public boolean validateBalance(BalanceValidator balanceValidator) {
try {
if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Unknown balance");
balanceValidator.setSecondLine("Balance cannot be verified. Swipe down to refresh.");
return false;
}
// Workaround before new back-end
// if (card.getRemainingSignatures() == card.getMaxSignatures()) {
// firstLine = "Verified balance";
// secondLine = "Balance confirmed in blockchain. ";
// secondLine += "Verified note identity. ";
// return;
// }
if (coinData.getBalanceUnconfirmed() != 0) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
return false;
}
if (coinData.isBalanceReceived() && coinData.isBalanceEqual()) {
balanceValidator.setScore(100);
balanceValidator.setFirstLine("Verified balance");
balanceValidator.setSecondLine("Balance confirmed in blockchain");
if (coinData.getBalanceInInternalUnits().isZero()) {
balanceValidator.setFirstLine("Empty wallet");
balanceValidator.setSecondLine("");
}
}
// rule 4 TODO: need to check SignedHashed against number of outputs in blockchain
// if((card.getRemainingSignatures() != card.getMaxSignatures()) && card.getBalance() != 0)
// {
// score = 80;
// firstLine = "Unguaranteed balance";
// secondLine = "Potential unsent transaction. Redeem immediately if accept. ";
// return;
// }
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
balanceValidator.setScore(80);
balanceValidator.setFirstLine("Verified offline balance");
balanceValidator.setSecondLine("Can't obtain balance from blockchain. Restore internet connection to be more confident. ");
}
// if(card.getFailedBalanceRequestCounter()!=0) {
// score -= 5 * card.getFailedBalanceRequestCounter();
// secondLine += "Not all nodes have returned balance. Swipe down or tap again. ";
// if(score <= 0)
// return;
// }
//
// if(card.isBalanceReceived() && !card.isBalanceEqual()) {
// score = 0;
// firstLine = "Disputed balance";
// secondLine += " Cannot obtain trusted balance at the moment. Try to tap and check this banknote later.";
// return;
// }
return true;
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
}
@Override
public Amount getBalance() {
if (!hasBalanceInfo()) return null;
return convertToAmount(coinData.getBalanceInInternalUnits());
}
@Override
public String evaluateFeeEquivalent(String fee) {
if (!coinData.getAmountEquivalentDescriptionAvailable()) return "";
try {
Amount feeAmount = new Amount(fee, getFeeCurrency());
return feeAmount.toEquivalentString(coinData.getRate());
} catch (Exception e) {
return "";
}
}
@Override
public String getBalanceEquivalent() {
if (coinData == null || !coinData.getAmountEquivalentDescriptionAvailable()) return "";
Amount balance = getBalance();
if (balance == null) return "";
return balance.toEquivalentString(coinData.getRate());
}
@Override
public String calculateAddress(byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException {
byte netSelectionByte;
switch (ctx.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 Amount convertToAmount(InternalAmount internalAmount) {
BigDecimal d = internalAmount.divide(new BigDecimal("100000000"));
return new Amount(d, getBalanceCurrency());
}
@Override
public Amount convertToAmount(String strAmount, String currency) {
return new Amount(strAmount, currency);
}
@Override
public InternalAmount convertToInternalAmount(Amount amount) {
BigDecimal d = amount.multiply(new BigDecimal("100000000"));
return new InternalAmount(d, "Satoshi");
}
@Override
public InternalAmount convertToInternalAmount(byte[] bytes) {
if (bytes == null) return null;
byte[] reversed = new byte[bytes.length];
for (int i = 0; i < bytes.length; i++) reversed[i] = bytes[bytes.length - i - 1];
return new InternalAmount(Util.byteArrayToLong(reversed), "Satoshi");
}
@Override
public byte[] convertToByteArray(InternalAmount internalAmount) {
byte[] bytes = Util.longToByteArray(internalAmount.longValueExact());
byte[] reversed = new byte[bytes.length];
for (int i = 0; i < bytes.length; i++) reversed[i] = bytes[bytes.length - i - 1];
return reversed;
}
@Override
public CoinData createCoinData() {
return new BtcData();
}
@Override
public String getUnspentInputsDescription() {
return coinData.getUnspentInputsDescription();
}
@Override
public SignTask.PaymentToSign constructPayment(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception {
final ArrayList<UnspentOutputInfo> unspentOutputs;
checkBlockchainDataExists();
String myAddress = ctx.getCoinData().getWallet();
byte[] pbKey = ctx.getCard().getWalletPublicKey();
// Build script for our address
List<BtcData.UnspentTransaction> rawTxList = coinData.getUnspentTransactions();
byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes;
// Collect unspent
unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend);
long fullAmount = 0;
for (int i = 0; i < unspentOutputs.size(); ++i) {
fullAmount += unspentOutputs.get(i).value;
}
long fees = convertToInternalAmount(feeValue).longValueExact();
long amount = convertToInternalAmount(amountValue).longValueExact();
long change = fullAmount - amount;
if (IncFee) {
amount = amount - fees;
} else {
change = change - fees;
}
final long amountFinal = amount;
final long changeFinal = change;
if (amount + fees > fullAmount) {
throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
}
final byte[][] txForSign = new byte[unspentOutputs.size()][];
final byte[][] bodyDoubleHash = new byte[unspentOutputs.size()][];
final byte[][] bodyHash = new byte[unspentOutputs.size()][];
for (int i = 0; i < unspentOutputs.size(); ++i) {
txForSign[i] = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change);
bodyHash[i] = Util.calculateSHA256(txForSign[i]);
bodyDoubleHash[i] = Util.calculateSHA256(bodyHash[i]);
}
return new SignTask.PaymentToSign() {
@Override
public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
return signingMethod == TangemCard.SigningMethod.Sign_Hash || signingMethod == TangemCard.SigningMethod.Sign_Raw;
}
@Override
public byte[][] getHashesToSign() throws Exception {
byte[][] dataForSign = new byte[unspentOutputs.size()][];
if (txForSign.length > 10) throw new Exception("To much hashes in one transaction!");
for (int i = 0; i < unspentOutputs.size(); ++i) {
dataForSign[i] = bodyDoubleHash[i];
}
return dataForSign;
}
@Override
public byte[] getRawDataToSign() throws Exception {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
for (int i = 0; i < txForSign.length; i++) {
if (i != 0 && txForSign[0].length != txForSign[i].length)
throw new Exception("Hashes length must be identical!");
bs.write(txForSign[i]);
}
return bs.toByteArray();
}
@Override
public String getHashAlgToSign() {
return "sha-256x2";
}
@Override
public byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception {
throw new Exception("Issuer validation not supported!");
}
@Override
public byte[] onSignCompleted(byte[] signFromCard) throws Exception {
for (int i = 0; i < unspentOutputs.size(); ++i) {
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64));
s = CryptoUtil.toCanonicalised(s);
unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey);
}
byte[] txForSend = BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal);
notifyOnNeedSendPayment(txForSend);
return txForSend;
}
};
}
@Override
public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) {
final ServerApiElectrum serverApiElectrum = new ServerApiElectrum();
ServerApiElectrum.ElectrumRequestDataListener electrumListener = new ServerApiElectrum.ElectrumRequestDataListener() {
@Override
public void onSuccess(ElectrumRequest electrumRequest) {
Log.i(TAG, "onSuccess: "+electrumRequest.getMethod());
if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetBalance)) {
try {
String walletAddress = electrumRequest.getParams().getString(0);
if (!walletAddress.equals(coinData.getWallet())) {
// todo - check
throw new Exception("Invalid wallet address in answer!");
}
Long confBalance = electrumRequest.getResult().getLong("confirmed");
Long unconfirmedBalance = electrumRequest.getResult().getLong("unconfirmed");
coinData.setBalanceReceived(true);
coinData.setBalanceConfirmed(confBalance);
coinData.setBalanceUnconfirmed(unconfirmedBalance);
coinData.setValidationNodeDescription(serverApiElectrum.getValidationNodeDescription());
} catch (JSONException e) {
e.printStackTrace();
Log.e(TAG, "FAIL METHOD_GetBalance JSONException");
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "FAIL METHOD_GetBalance Exception");
}
} else if (electrumRequest.isMethod(ElectrumRequest.METHOD_ListUnspent)) {
try {
String walletAddress = electrumRequest.getParams().getString(0);
JSONArray jsUnspentArray = electrumRequest.getResultArray();
try {
coinData.getUnspentTransactions().clear();
for (int i = 0; i < jsUnspentArray.length(); i++) {
JSONObject jsUnspent = jsUnspentArray.getJSONObject(i);
BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction();
trUnspent.txID = jsUnspent.getString("tx_hash");
trUnspent.Amount = jsUnspent.getInt("value");
trUnspent.Height = jsUnspent.getInt("height");
coinData.getUnspentTransactions().add(trUnspent);
}
} catch (JSONException e) {
e.printStackTrace();
Log.e(TAG, "FAIL METHOD_ListUnspent JSONException");
}
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) {
if (blockchainRequestsCallbacks.allowAdvance()) {
serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash));
} else {
ctx.setError("Terminated by user");
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
} else if (electrumRequest.isMethod(ElectrumRequest.METHOD_GetTransaction)) {
try {
String txHash = electrumRequest.txHash;
String raw = electrumRequest.getResultString();
for (BtcData.UnspentTransaction tx : coinData.getUnspentTransactions()) {
if (tx.txID.equals(txHash))
tx.Raw = raw;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
if (serverApiElectrum.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
}else{
blockchainRequestsCallbacks.onProgress();
}
}
@Override
public void onFail(ElectrumRequest electrumRequest) {
Log.i(TAG, "onFail: "+electrumRequest.getMethod()+" "+electrumRequest.getError());
ctx.setError(electrumRequest.getError());
if (serverApiElectrum.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError());
}else{
blockchainRequestsCallbacks.onProgress();
}
}
};
serverApiElectrum.setElectrumRequestData(electrumListener);
serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.checkBalance(coinData.getWallet()));
serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.listUnspent(coinData.getWallet()));
}
protected Integer calculateEstimatedTransactionSize(String outputAddress, String outAmount) {
//todo - правильней было бы использовать constructPayment
try {
// String myAddress = coinData.getWallet();
// byte[] pbKey = ctx.getCard().getWalletPublicKey();
// byte[] pbComprKey = ctx.getCard().getWalletPublicKeyRar();
//
// // build script for our address
// List<BtcData.UnspentTransaction> rawTxList = coinData.getUnspentTransactions();
// byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes;
//
// // collect unspent
// ArrayList<UnspentOutputInfo> unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend);
//
// Long fullAmount = 0L;
// for (int i = 0; i < unspentOutputs.size(); i++) {
// fullAmount += unspentOutputs.get(i).value;
// }
//
// // get first unspent
//// val outPut = unspentOutputs[0]
//// val outPutIndex = outPut.outputIndex
//
// // get prev TX id;
//// val prevTXID = rawTxList[0].txID//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2";
//
// Long fees = FormatUtil.ConvertStringToLong("0.00");
// Long amount = FormatUtil.ConvertStringToLong(outAmount);
// 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, myAddress, 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[i].bodyDoubleHash = doubleHashData
//// unspentOutputs[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, 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, myAddress, unspentOutputs, amount, change);
SignTask.PaymentToSign ps=constructPayment(new Amount(outAmount, getBalanceCurrency()),new Amount("0.00",getFeeCurrency()), true, outputAddress );
OnNeedSendPayment onNeedSendPaymentBackup=onNeedSendPayment;
onNeedSendPayment=(tx)->{}; // empty function to bypass exception
byte[][] hashesToSign=ps.getHashesToSign();
byte[] signFromCard = new byte[64 * hashesToSign.length];
byte[] txForSend=ps.onSignCompleted(signFromCard);
onNeedSendPayment=onNeedSendPaymentBackup;
Log.e(TAG,"txForSend.length="+String.valueOf(txForSend.length));
return txForSend.length;
// Log.e(TAG,"txForSend.length="+String.valueOf(txForSend.length)+" realTX.length="+String.valueOf(realTX.length));
//
// return realTX.length;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Can't calculate transaction size -> use default!");
return 256;
}
}
@Override
public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception {
final int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString());
Log.e(TAG, String.format("Estimated tx size %d", calcSize));
coinData.minFee = null;
coinData.maxFee = null;
coinData.normalFee = null;
final ServerApiCommon serverApiCommon = new ServerApiCommon();
final ServerApiCommon.EstimateFeeListener estimateFeeListener = new ServerApiCommon.EstimateFeeListener() {
@Override
public void onSuccess(int blockCount, String estimateFeeResponse) {
BigDecimal fee = new BigDecimal(estimateFeeResponse); // BTC per 1 kb
if (fee.equals(BigDecimal.ZERO)) {
if (blockchainRequestsCallbacks.allowAdvance()) {
serverApiCommon.estimateFee(blockCount);
}
return;
}
if (calcSize != 0) {
fee = fee.multiply(new BigDecimal(calcSize)).divide(new BigDecimal(1024), BigDecimal.ROUND_DOWN); // per Kb -> per byte
} else {
if (blockchainRequestsCallbacks.allowAdvance()) {
serverApiCommon.estimateFee(blockCount);
}
return;
}
fee = fee.setScale(8, RoundingMode.DOWN);
switch (blockCount) {
case ServerApiCommon.ESTIMATE_FEE_MINIMAL:
coinData.minFee = new CoinEngine.Amount(fee, getFeeCurrency());
break;
case ServerApiCommon.ESTIMATE_FEE_NORMAL:
coinData.normalFee = new CoinEngine.Amount(fee, getFeeCurrency());
break;
case ServerApiCommon.ESTIMATE_FEE_PRIORITY:
coinData.maxFee = new CoinEngine.Amount(fee, getFeeCurrency());
break;
}
if(coinData.minFee!=null && coinData.normalFee!=null && coinData.maxFee!=null ) {
blockchainRequestsCallbacks.onComplete(true);
}else{
blockchainRequestsCallbacks.onProgress();
}
}
@Override
public void onFail(int blockCount, String message) {
// TODO - add fail counter to terminate after NNN tries
if (blockchainRequestsCallbacks.allowAdvance()) {
serverApiCommon.estimateFee(blockCount);
return;
}
ctx.setError(ctx.getContext().getString(R.string.cannot_calculate_fee_wrong_data_received_from_node));
blockchainRequestsCallbacks.onComplete(false);
}
};
serverApiCommon.setEstimateFee(estimateFeeListener);
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY);
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL);
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL);
}
@Override
public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) {
final ServerApiElectrum serverApiElectrum = new ServerApiElectrum();
final String txStr = BTCUtils.toHex(txForSend);
ServerApiElectrum.ElectrumRequestDataListener electrumListener = new ServerApiElectrum.ElectrumRequestDataListener() {
@Override
public void onSuccess(ElectrumRequest electrumRequest) {
if (electrumRequest.isMethod(ElectrumRequest.METHOD_SendTransaction)) {
try {
String resultString = electrumRequest.getResultString();
if (resultString == null || resultString.isEmpty()) {
ctx.setError("Rejected by node: " + electrumRequest.getError());
blockchainRequestsCallbacks.onComplete(false);
}else {
ctx.setError(null);
blockchainRequestsCallbacks.onComplete(true);
}
} catch (Exception e) {
if (e.getMessage() != null) {
ctx.setError(e.getMessage());
blockchainRequestsCallbacks.onComplete(false);
} else {
ctx.setError(e.getClass().getName());
blockchainRequestsCallbacks.onComplete(false);
}
}
}
}
@Override
public void onFail(ElectrumRequest electrumRequest) {
ctx.setError(electrumRequest.getError());
blockchainRequestsCallbacks.onComplete(false);
}
};
serverApiElectrum.setElectrumRequestData(electrumListener);
serverApiElectrum.electrumRequestData(ctx, ElectrumRequest.broadcast(ctx.getCoinData().getWallet(), txStr));
}
}

View file

@ -1,17 +0,0 @@
package com.tangem.domain.wallet.ltc
enum class LitecoinNode(val host: String, val port: Int, val proto: String) {
N_001("node.ispol.sk", 50004, "ssl"),
N_002("electrum-ltc.klippb.org", 50002, "ssl"),
N_003("backup.electrum-ltc.org", 443, "ssl"),
N_004("electrum-ltc.petrkr.net", 60002, "ssl"),
N_005("technetium.network", 50003, "ssl"),
N_006("167.99.146.166", 50002, "ssl"),
N_007("electrum-ltc.bysh.me", 50002, "ssl"),
N_008("e-3.claudioboxx.com", 50004, "ssl"),
N_009("electrum-ltc.wilv.in", 50002, "ssl"),
N_010("ltc.rentonisk.com", 50002, "ssl"),
N_011("e-1.claudioboxx.com", 50004, "ssl"),
N_012("electrum.ltc.xurious.com", 50002, "ssl"),
N_013("ltc01.knas.systems", 50004, "ssl"),
}

View file

@ -1,298 +0,0 @@
package com.tangem.presentation.activity
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.TextWatcher
import android.view.KeyEvent
import android.view.View
import android.widget.Toast
import com.tangem.Constant
import com.tangem.data.Blockchain
import com.tangem.domain.wallet.CoinEngine
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.event.TransactionFinishWithError
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.tangemcard.data.TangemCard
import com.tangem.tangemcard.data.loadFromBundle
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_confirm_payment.*
import org.greenrobot.eventbus.EventBus
import java.io.IOException
import java.util.*
class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
private var nfcManager: NfcManager? = null
private lateinit var ctx: TangemContext
private lateinit var amount: CoinEngine.Amount
private var isIncludeFee: Boolean = true
private var requestPIN2Count = 0
private var nodeCheck = true
private var dtVerified: Date? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_confirm_payment)
nfcManager = NfcManager(this, this)
ctx = TangemContext.loadFromBundle(this, intent.extras)
val engine = CoinEngineFactory.create(ctx)
val html = Html.fromHtml(engine!!.balanceHTML)
tvBalance.text = html
isIncludeFee = intent.getBooleanExtra(Constant.EXTRA_FEE_INCLUDED, true)
if (isIncludeFee)
tvIncFee.setText(R.string.including_fee)
else
tvIncFee.setText(R.string.not_including_fee)
amount = CoinEngine.Amount(intent.getStringExtra(Constant.EXTRA_AMOUNT), intent.getStringExtra(Constant.EXTRA_AMOUNT_CURRENCY))
if (ctx.blockchain == Blockchain.Token && amount.currency != Blockchain.Ethereum.currency)
tvIncFee.visibility = View.INVISIBLE
else
tvIncFee.visibility = View.VISIBLE
etAmount.setText(amount.toValueString())
tvCurrency.text = engine.balanceCurrency
tvCurrency2.text = engine.feeCurrency
tvCardID.text = ctx.card!!.cidDescription
etWallet.setText(intent.getStringExtra(Constant.EXTRA_TARGET_ADDRESS))
etFee.setText("")
btnSend.visibility = View.INVISIBLE
for (lol in rgFee.touchables) {
lol.isEnabled = !(ctx.blockchain == Blockchain.Ethereum || ctx.blockchain == Blockchain.EthereumTestNet || ctx.blockchain == Blockchain.Token || ctx.blockchain == Blockchain.BitcoinCash || ctx.blockchain == Blockchain.Litecoin || ctx.blockchain == Blockchain.Stellar || ctx.blockchain == Blockchain.StellarTestNet)
}
// set listeners
rgFee.setOnCheckedChangeListener { _, checkedId -> doSetFee(checkedId) }
etFee.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
try {
val engine = CoinEngineFactory.create(ctx)
val eqFee = engine!!.evaluateFeeEquivalent(etFee!!.text.toString())
tvFeeEquivalent.text = eqFee
if (!ctx.coinData!!.amountEquivalentDescriptionAvailable) {
tvFeeEquivalent.error = "Service unavailable"
tvCurrency2.visibility = View.GONE
tvFeeEquivalent.visibility = View.GONE
} else
tvFeeEquivalent.error = null
} catch (e: Exception) {
e.printStackTrace()
tvFeeEquivalent.text = ""
}
}
override fun afterTextChanged(s: Editable) {
}
})
btnSend.setOnClickListener {
val calendar = Calendar.getInstance()
calendar.add(Calendar.MINUTE, -1)
if (dtVerified == null || dtVerified!!.before(calendar.time)) {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.the_obtained_data_is_outdated_try_again))
return@setOnClickListener
}
val engineCoin = CoinEngineFactory.create(ctx)
if (engineCoin!!.isNeedCheckNode && !nodeCheck) {
Toast.makeText(baseContext, getString(R.string.cannot_reach_current_active_blockchain_node_try_again), Toast.LENGTH_LONG).show()
return@setOnClickListener
}
val txFee = engineCoin.convertToAmount(etFee.text.toString(), tvCurrency2.text.toString())
val txAmount = engineCoin.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString())
if (!engineCoin.hasBalanceInfo()) {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.cannot_check_balance_no_connection_with_blockchain_nodes))
return@setOnClickListener
} else if (!engineCoin.isBalanceNotZero) {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.the_wallet_is_empty))
return@setOnClickListener
} else if (!engineCoin.isExtractPossible) {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.please_wait_for_confirmation_of_incoming_transaction))
return@setOnClickListener
}
if (!engineCoin.checkNewTransactionAmountAndFee(txAmount, txFee, isIncludeFee)) {
finishWithError(Activity.RESULT_CANCELED, getString(R.string.not_enough_funds_on_your_card))
return@setOnClickListener
}
requestPIN2Count = 0
val intent = Intent(baseContext, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_)
}
val coinEngine = CoinEngineFactory.create(ctx)
progressBar.visibility = View.VISIBLE
coinEngine!!.requestFee(
object : CoinEngine.BlockchainRequestsCallbacks {
override fun onComplete(success: Boolean) {
if (success) {
onProgress()
progressBar.visibility = View.INVISIBLE
dtVerified = Date()
} else {
finishWithError(Activity.RESULT_CANCELED, ctx.error)
}
}
override fun onProgress() {
doSetFee(rgFee.checkedRadioButtonId)
}
override fun allowAdvance(): Boolean {
return UtilHelper.isOnline(this@ConfirmPaymentActivity)
}
},
etWallet.text.toString(),
amount)
}
public override fun onResume() {
super.onResume()
nfcManager!!.onResume()
}
public override fun onPause() {
super.onPause()
nfcManager!!.onPause()
}
public override fun onStop() {
super.onStop()
nfcManager!!.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constant.REQUEST_CODE_SIGN_PAYMENT) {
if (data != null && data.extras != null) {
if (data.extras!!.containsKey("UID") && data.extras!!.containsKey("Card")) {
val updatedCard = TangemCard(data.getStringExtra("UID"))
updatedCard.loadFromBundle(data.getBundleExtra("Card"))
ctx.card = updatedCard
}
}
if (resultCode == Constant.RESULT_INVALID_PIN_ && requestPIN2Count < 2) {
requestPIN2Count++
val intent = Intent(baseContext, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2_)
return
}
setResult(resultCode, data)
finish()
} else if (requestCode == Constant.REQUEST_CODE_REQUEST_PIN2_) {
if (resultCode == Activity.RESULT_OK) {
val intent = Intent(baseContext, SignPaymentActivity::class.java)
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString())
intent.putExtra(Constant.EXTRA_AMOUNT, etAmount.text.toString())
intent.putExtra(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString())
intent.putExtra(Constant.EXTRA_FEE, etFee.text.toString())
intent.putExtra(Constant.EXTRA_FEE_CURRENCY, tvCurrency2.text.toString())
intent.putExtra(Constant.EXTRA_FEE_INCLUDED, isIncludeFee)
startActivityForResult(intent, Constant.REQUEST_CODE_SIGN_PAYMENT)
} else
Toast.makeText(baseContext, R.string.pin_2_is_required_to_sign_the_payment, Toast.LENGTH_LONG).show()
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
val intent = Intent()
setResult(Activity.RESULT_CANCELED, intent)
finish()
return true
}
}
return super.onKeyDown(keyCode, event)
}
override fun onTagDiscovered(tag: Tag) {
try {
nfcManager!!.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
private fun doSetFee(checkedRadioButtonId: Int) {
var txtFee = ""
when (checkedRadioButtonId) {
R.id.rbMinimalFee ->
if (ctx.coinData.minFee != null) {
txtFee = ctx.coinData.minFee!!.toValueString()
btnSend.visibility = View.VISIBLE
} else
btnSend.visibility = View.INVISIBLE
R.id.rbNormalFee ->
if (ctx.coinData.normalFee != null) {
txtFee = ctx.coinData.normalFee!!.toValueString()
btnSend.visibility = View.VISIBLE
} else
btnSend.visibility = View.INVISIBLE
R.id.rbMaximumFee ->
if (ctx.coinData.maxFee != null) {
txtFee = ctx.coinData.maxFee!!.toValueString()
btnSend.visibility = View.VISIBLE
} else
btnSend.visibility = View.INVISIBLE
}
etFee.setText(txtFee.replace(',', '.'))
}
private fun finishWithError(errorCode: Int, message: String) {
val transactionFinishWithError = TransactionFinishWithError()
transactionFinishWithError.message = message
EventBus.getDefault().post(transactionFinishWithError)
val intent = Intent()
intent.putExtra("message", message)
setResult(errorCode, intent)
finish()
}
}

View file

@ -1,210 +0,0 @@
package com.tangem.presentation.activity
import android.app.Activity
import android.content.Context
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.view.View
import android.widget.ProgressBar
import android.widget.Toast
import com.tangem.App
import com.tangem.Constant
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.tangemcard.android.reader.NfcReader
import com.tangem.tangemcard.data.asBundle
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.tasks.CreateNewWalletTask
import com.tangem.tangemcard.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_create_new_wallet.*
class CreateNewWalletActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
fun callingIntent(context: Context, ctx: TangemContext): Intent {
val intent = Intent(context, CreateNewWalletActivity::class.java)
intent.putExtra("UID", ctx.card!!.uid)
intent.putExtra("Card", ctx.card!!.asBundle)
return intent
}
}
private lateinit var ctx: TangemContext
private lateinit var nfcManager: NfcManager
private var createNewWalletTask: CreateNewWalletTask? = null
private var lastReadSuccess = true
private var progressBar: ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create_new_wallet)
nfcManager = NfcManager(this, this)
ctx = TangemContext.loadFromBundle(this, intent.extras)
tvCardId.text = ctx.card!!.cidDescription
progressBar = findViewById(R.id.progressBar)
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
// Log.v(TAG, "UID: " + sUID);
if (sUID == ctx.card!!.uid) {
if (lastReadSuccess) {
isoDep.timeout = ctx.card!!.pauseBeforePIN2 + 5000
} else {
isoDep.timeout = ctx.card!!.pauseBeforePIN2 + 65000
}
createNewWalletTask = CreateNewWalletTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
createNewWalletTask!!.start()
} else {
// Log.d(TAG, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")");
nfcManager.ignoreTag(isoDep.tag)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
public override fun onResume() {
super.onResume()
nfcManager.onResume()
}
public override fun onPause() {
nfcManager.onPause()
if (createNewWalletTask != null)
createNewWalletTask!!.cancel(true)
super.onPause()
}
public override fun onStop() {
// dismiss enable NFC dialog
nfcManager.onStop()
if (createNewWalletTask != null)
createNewWalletTask!!.cancel(true)
super.onStop()
}
override fun onReadStart(cardProtocol: CardProtocol) {
progressBar!!.post {
progressBar!!.visibility = View.VISIBLE
progressBar!!.progress = 5
}
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
createNewWalletTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.GREEN)
val intent = Intent()
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
setResult(Activity.RESULT_OK, intent)
finish()
}
} else {
lastReadSuccess = false
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
val intent = Intent()
intent.putExtra("message", "Cannot create wallet. Make sure you enter correct PIN2!")
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card!!.asBundle)
setResult(Constant.RESULT_INVALID_PIN, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
} else {
progressBar!!.post {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
}
} else {
Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
}
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar!!.post { progressBar!!.progress = progress }
}
override fun onReadCancel() {
createNewWalletTask = null
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(this, msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this)
}
}

View file

@ -1,269 +0,0 @@
package com.tangem.presentation.activity
import android.app.Activity
import android.content.Context
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.text.Html
import android.view.View
import android.widget.Toast
import com.tangem.App
import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.tangemcard.android.reader.NfcReader
import com.tangem.tangemcard.data.TangemCard
import com.tangem.tangemcard.data.asBundle
import com.tangem.tangemcard.data.loadFromBundle
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.tasks.VerifyCardTask
import com.tangem.tangemcard.util.Util
import com.tangem.util.LOG
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_empty_wallet.*
import javax.inject.Inject
class EmptyWalletActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
val TAG: String = EmptyWalletActivity::class.java.simpleName
fun callingIntent(context: Context, ctx: TangemContext): Intent {
val intent = Intent(context, EmptyWalletActivity::class.java)
ctx.saveToIntent(intent)
return intent
}
}
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private var lastReadSuccess = true
private var verifyCardTask: VerifyCardTask? = null
private var requestPIN2Count = 0
@Inject
internal lateinit var navigator: Navigator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_empty_wallet)
App.getNavigatorComponent().inject(this)
nfcManager = NfcManager(this, this)
ctx = TangemContext.loadFromBundle(this, intent.extras)
tvIssuer.text = ctx.card!!.issuerDescription
if (ctx.card!!.tokenSymbol.length > 1) {
val html = Html.fromHtml(ctx.blockchainName)
tvBlockchain.text = html
} else
tvBlockchain.text = ctx.blockchainName
tvCardID.text = ctx.card!!.cidDescription
imgBlockchain.setImageResource(ctx.blockchain.getImageResource(this, ctx.card!!.tokenSymbol))
if (ctx.card!!.useDefaultPIN1()) {
imgPIN.setImageResource(R.drawable.unlock_pin1)
imgPIN.setOnClickListener { Toast.makeText(this@EmptyWalletActivity, R.string.this_banknote_protected_default_PIN1_code, Toast.LENGTH_LONG).show() }
} else {
imgPIN.setImageResource(R.drawable.lock_pin1)
imgPIN.setOnClickListener { Toast.makeText(this@EmptyWalletActivity, R.string.this_banknote_protected_user_PIN1_code, Toast.LENGTH_LONG).show() }
}
if (ctx.card!!.pauseBeforePIN2 > 0 && (ctx.card!!.useDefaultPIN2()!! || !ctx.card!!.useSmartSecurityDelay())) {
imgPIN2orSecurityDelay.setImageResource(R.drawable.timer)
imgPIN2orSecurityDelay.setOnClickListener { Toast.makeText(this@EmptyWalletActivity, String.format(getString(R.string.this_banknote_will_enforce), ctx.card!!.pauseBeforePIN2 / 1000.0), Toast.LENGTH_LONG).show() }
} else if (ctx.card!!.useDefaultPIN2()!!) {
imgPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2)
imgPIN2orSecurityDelay.setOnClickListener { Toast.makeText(this@EmptyWalletActivity, R.string.this_banknote_protected_default_PIN2_code, Toast.LENGTH_LONG).show() }
} else {
imgPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2)
imgPIN2orSecurityDelay.setOnClickListener { Toast.makeText(this@EmptyWalletActivity, R.string.this_banknote_protected_user_PIN2_code, Toast.LENGTH_LONG).show() }
}
if (ctx.card!!.useDevelopersFirmware()!!) {
imgDeveloperVersion.setImageResource(R.drawable.ic_developer_version)
imgDeveloperVersion.visibility = View.VISIBLE
imgDeveloperVersion.setOnClickListener { Toast.makeText(this@EmptyWalletActivity, R.string.unlocked_banknote_only_development_use, Toast.LENGTH_LONG).show() }
} else
imgDeveloperVersion.visibility = View.INVISIBLE
// set listeners
btnNewWallet.setOnClickListener {
requestPIN2Count = 0
val intent = Intent(baseContext, PinRequestActivity::class.java)
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
intent.putExtra("UID", ctx.card!!.uid)
intent.putExtra("Card", ctx.card!!.asBundle)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2)
}
}
public override fun onResume() {
super.onResume()
nfcManager.onResume()
}
public override fun onPause() {
super.onPause()
nfcManager.onPause()
}
public override fun onStop() {
super.onStop()
nfcManager.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == Constant.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.extras!!.containsKey("UID") && data.extras!!.containsKey("Card")) {
val updatedCard = TangemCard(data.getStringExtra("UID"))
updatedCard.loadFromBundle(data.getBundleExtra("Card"))
ctx.card = updatedCard
}
if (resultCode == Constant.RESULT_INVALID_PIN && requestPIN2Count < 2) {
requestPIN2Count++
val intent = Intent(baseContext, PinRequestActivity::class.java)
intent.putExtra("mode", PinRequestActivity.Mode.RequestPIN2.toString())
intent.putExtra("UID", ctx.card!!.uid)
intent.putExtra("Card", ctx.card!!.asBundle)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2)
return
}
}
setResult(resultCode, data)
finish()
} else if (requestCode == Constant.REQUEST_CODE_REQUEST_PIN2) {
if (resultCode == Activity.RESULT_OK) {
navigator.showCreateNewWallet(this, ctx)
}
}
}
override fun onTagDiscovered(tag: Tag) {
try {
val isoDep = IsoDep.get(tag)
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
if (ctx.card!!.uid != sUID) {
LOG.d(TAG, "Invalid UID: $sUID")
nfcManager.ignoreTag(isoDep.tag)
return
} else {
LOG.d(TAG, "UID: $sUID")
}
if (lastReadSuccess) {
isoDep.timeout = 1000
} else {
isoDep.timeout = 65000
}
//lastTag = tag;
verifyCardTask = VerifyCardTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, App.firmwaresStorage, this)
verifyCardTask!!.start()
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onReadStart(cardProtocol: CardProtocol) {
progressBar!!.post {
progressBar!!.visibility = View.VISIBLE
progressBar!!.progress = 5
}
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
verifyCardTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.GREEN)
// val intent = Intent(this@EmptyWalletActivity, VerifyCardActivity::class.java)
// intent.putExtra("UID", cardProtocol.card.uid)
// intent.putExtra("Card", cardProtocol.card.asBundle)
// startActivityForResult(intent, REQUEST_CODE_VERIFY_CARD)
//addCard(cardProtocol.getCard());
}
} else {
// remove last UIDs because of error and no card read
progressBar!!.post {
lastReadSuccess = false
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
}
} else {
Toast.makeText(this@EmptyWalletActivity, R.string.try_to_scan_again, Toast.LENGTH_LONG).show()
}
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar!!.post { progressBar!!.progress = progress }
}
override fun onReadCancel() {
verifyCardTask = null
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(this, msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this)
}
}

View file

@ -1,422 +0,0 @@
package com.tangem.presentation.activity
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.IsoDep
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.PopupMenu
import android.util.Log
import android.view.Menu
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.RelativeLayout
import android.widget.Toast
import com.scottyab.rootbeer.RootBeer
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.Logger
import com.tangem.data.network.ServerApiCommon
import com.tangem.tangemcard.android.nfc.DeviceNFCAntennaLocation
import com.tangem.tangemcard.tasks.ReadCardInfoTask
import com.tangem.di.Navigator
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.RootFoundDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.tangemcard.android.reader.NfcReader
import com.tangem.tangemcard.data.TangemCard
import com.tangem.tangemcard.data.loadFromBundle
import com.tangem.tangemcard.data.saveToBundle
import com.tangem.util.CommonUtil
import com.tangem.util.LOG
import com.tangem.util.PhoneUtility
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
import java.util.*
import javax.inject.Inject
class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications, PopupMenu.OnMenuItemClickListener {
companion object {
val TAG: String = MainActivity::class.java.simpleName
fun callingIntent(context: Context) = Intent(context, MainActivity::class.java)
}
private lateinit var nfcManager: NfcManager
private var zipFile: File? = null
private var antenna: DeviceNFCAntennaLocation? = null
private var unsuccessReadCount = 0
private var lastTag: Tag? = null
private var readCardInfoTask: ReadCardInfoTask? = null
private var onNfcReaderCallback: NfcAdapter.ReaderCallback? = null
@Inject
internal lateinit var navigator: Navigator
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag != null && onNfcReaderCallback != null)
onNfcReaderCallback!!.onTagDiscovered(tag)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
App.getNavigatorComponent().inject(this)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
nfcManager = NfcManager(this, this)
verifyPermissions()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
setNfcAdapterReaderCallback(this)
rippleBackgroundNfc.startRippleAnimation()
antenna = DeviceNFCAntennaLocation()
antenna!!.getAntennaLocation()
// set card orientation
when (antenna!!.orientation) {
DeviceNFCAntennaLocation.CARD_ORIENTATION_HORIZONTAL -> {
ivHandCardHorizontal.visibility = View.VISIBLE
ivHandCardVertical.visibility = View.GONE
}
DeviceNFCAntennaLocation.CARD_ORIENTATION_VERTICAL -> {
ivHandCardVertical.visibility = View.VISIBLE
ivHandCardHorizontal.visibility = View.GONE
}
}
// set card z position
when (antenna!!.z) {
DeviceNFCAntennaLocation.CARD_ON_BACK -> llHand.elevation = 0.0f
DeviceNFCAntennaLocation.CARD_ON_FRONT -> llHand.elevation = 30.0f
}
// set phone name
if (antenna!!.fullName != "")
tvNFCHint.text = String.format(getString(R.string.scan_banknote), antenna!!.fullName)
else
tvNFCHint.text = String.format(getString(R.string.scan_banknote), getString(R.string.phone))
animate()
// NFC
val intent = intent
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag != null && onNfcReaderCallback != null) {
onNfcReaderCallback!!.onTagDiscovered(tag)
}
}
// check if root device
val rootBeer = RootBeer(this)
if (rootBeer.isRootedWithoutBusyBoxCheck && !BuildConfig.DEBUG)
RootFoundDialog().show(supportFragmentManager, RootFoundDialog.TAG)
// set listeners
fab.setOnClickListener { showMenu(it) }
val apiHelper = ServerApiCommon()
apiHelper.setLastVersionListener { response ->
try {
if (response.isNullOrEmpty()) return@setLastVersionListener
val responseVersionName = response.trim(' ', '\n', '\r', '\t')
val responseBuildVersion = responseVersionName.split('.').last()
val appBuildVersion = BuildConfig.VERSION_NAME.split('.').last()
if (responseBuildVersion.toInt() > appBuildVersion.toInt()) Toast.makeText(this, "There is a new application version: $responseVersionName", Toast.LENGTH_LONG).show()
} catch (E: Exception) {
E.printStackTrace()
}
}
apiHelper.requestLastVersion()
}
private fun verifyPermissions() {
NfcManager.verifyPermissions(this)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
Log.e("QRScanActivity", "User hasn't granted permission to use camera")
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), Constant.REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
Constant.REQUEST_CODE_SEND_EMAIL -> {
if (zipFile != null) {
zipFile!!.delete()
zipFile = null
}
}
Constant.REQUEST_CODE_ENTER_PIN_ACTIVITY -> {
if (resultCode == Activity.RESULT_OK && lastTag != null)
onTagDiscovered(lastTag!!)
else
ReadCardInfoTask.resetLastReadInfo()
}
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun onMenuItemClick(item: MenuItem): Boolean {
return onOptionsItemSelected(item)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
if (BuildConfig.DEBUG) {
for (i in 0 until menu.size()) menu.getItem(i).isVisible = true
}
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
when (id) {
R.id.sendLogs -> {
var f: File? = null
try {
f = Logger.collectLogs(this)
if (f != null) {
LOG.e(TAG, String.format("Collect %d log bytes", f.length()))
CommonUtil.sendEmail(this, zipFile, TAG, "Logs", PhoneUtility.getDeviceInfo(), arrayOf(f))
} else {
LOG.e(TAG, "Can't create temporarily log file")
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
if (f != null && f.exists())
f.delete()
}
return true
}
R.id.managePIN -> {
navigator.showPinSave(this, false)
return true
}
R.id.managePIN2 -> {
navigator.showPinSave(this, true)
return true
}
R.id.about -> {
navigator.showLogo(this, false)
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
LOG.e(TAG, "setTimeout(" + (1000 + 3000 * unsuccessReadCount) + ")")
if (unsuccessReadCount < 2) {
isoDep.timeout = 2000 + 5000 * unsuccessReadCount
} else {
isoDep.timeout = 90000
}
lastTag = tag
readCardInfoTask = ReadCardInfoTask(NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
readCardInfoTask!!.start()
LOG.i(TAG, "onTagDiscovered " + Arrays.toString(tag.id))
} catch (e: Exception) {
e.printStackTrace()
nfcManager.notifyReadResult(false)
}
}
public override fun onResume() {
super.onResume()
animate()
ReadCardInfoTask.resetLastReadInfo()
nfcManager.onResume()
}
public override fun onPause() {
nfcManager.onPause()
readCardInfoTask?.cancel(true)
super.onPause()
}
public override fun onStop() {
// dismiss enable NFC dialog
nfcManager.onStop()
readCardInfoTask?.cancel(true)
super.onStop()
}
override fun onReadStart(cardProtocol: CardProtocol) {
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
readCardInfoTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
nfcManager.notifyReadResult(true)
rlProgressBar.post {
rlProgressBar.visibility = View.GONE
// TODO - ??? remove save and load???
val cardInfo = Bundle()
cardInfo.putString("UID", cardProtocol.card.uid)
val bCard = Bundle()
cardProtocol.card.saveToBundle(bCard)
cardInfo.putBundle("Card", bCard)
val uid = cardInfo.getString("UID")
val card = TangemCard(uid)
card.loadFromBundle(cardInfo.getBundle("Card"))
val ctx = TangemContext(card)
when {
card.status == TangemCard.Status.Loaded -> lastTag?.let {
val engineCoin = CoinEngineFactory.create(ctx)
?: throw CardProtocol.TangemException("Can't create CoinEngine!")
engineCoin.defineWallet()
//mCard.setWallet(Blockchain.calculateWalletAddress(mCard, pkUncompressed));
navigator.showLoadedWallet(this, it, ctx)
}
card.status == TangemCard.Status.Empty -> navigator.showEmptyWallet(this, ctx)
card.status == TangemCard.Status.Purged -> Toast.makeText(this, R.string.erased_wallet, Toast.LENGTH_SHORT).show()
card.status == TangemCard.Status.NotPersonalized -> Toast.makeText(this, R.string.not_personalized, Toast.LENGTH_SHORT).show()
else -> lastTag?.let { navigator.showLoadedWallet(this, it, ctx) }
}
}
} else {
// remove last UIDs because of error and no card read
rlProgressBar.post {
Toast.makeText(this, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
unsuccessReadCount++
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN)
navigator.showPinRequest(this, PinRequestActivity.Mode.RequestPIN.toString())
else {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported)
if (!NoExtendedLengthSupportDialog.allReadyShowed)
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
lastTag = null
ReadCardInfoTask.resetLastReadInfo()
nfcManager.notifyReadResult(false)
}
}
}
}
rlProgressBar.postDelayed({
try {
rlProgressBar.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadCancel() {
readCardInfoTask = null
ReadCardInfoTask.resetLastReadInfo()
rlProgressBar.postDelayed({
try {
rlProgressBar.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(Objects.requireNonNull(this), msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(Objects.requireNonNull(this), timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(Objects.requireNonNull(this))
}
private fun setNfcAdapterReaderCallback(callback: NfcAdapter.ReaderCallback) {
onNfcReaderCallback = callback
}
private fun animate() {
val lp = llHand.layoutParams as RelativeLayout.LayoutParams
val lp2 = llNfc.layoutParams as RelativeLayout.LayoutParams
val dp = resources.displayMetrics.density
val lm = dp * (69 + antenna!!.x * 75)
lp.topMargin = (dp * (-100 + antenna!!.y * 250)).toInt()
lp2.topMargin = (dp * (-125 + antenna!!.y * 250)).toInt()
llNfc.layoutParams = lp2
val a = object : Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
lp.leftMargin = (lm * interpolatedTime).toInt()
llHand.layoutParams = lp
}
}
a.duration = 2000
a.interpolator = DecelerateInterpolator()
llHand.startAnimation(a)
}
private fun showMenu(v: View) {
val popup = PopupMenu(this, v)
val inflater = popup.menuInflater
inflater.inflate(R.menu.menu_main, popup.menu)
if (BuildConfig.DEBUG) {
popup.menu.findItem(R.id.managePIN).isEnabled = true
popup.menu.findItem(R.id.managePIN2).isEnabled = true
popup.menu.findItem(R.id.sendLogs).isVisible = true
}
popup.setOnMenuItemClickListener(this)
popup.show()
}
}

View file

@ -1,198 +0,0 @@
package com.tangem.presentation.activity
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
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.Html
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.Blockchain
import com.tangem.di.Navigator
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_prepare_payment.*
import java.io.IOException
import javax.inject.Inject
class PreparePaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
companion object {
val TAG: String = PreparePaymentActivity::class.java.simpleName
fun callingIntent(context: Context, ctx: TangemContext): Intent {
val intent = Intent(context, PreparePaymentActivity::class.java)
ctx.saveToIntent(intent)
return intent
}
}
private lateinit var ctx: TangemContext
private var nfcManager: NfcManager? = null
@Inject
internal lateinit var navigator: Navigator
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_prepare_payment)
App.getNavigatorComponent().inject(this)
nfcManager = NfcManager(this, this)
ctx = TangemContext.loadFromBundle(this, intent.extras)
tvCardID.text = ctx.card!!.cidDescription
val engine = CoinEngineFactory.create(ctx)
val html = Html.fromHtml(engine!!.balanceHTML)
tvBalance.text = html
//TODO - to engine
if (ctx.blockchain == Blockchain.Token && engine.balance.currency!=Blockchain.Ethereum.currency) {
rgIncFee!!.visibility = View.INVISIBLE
} else {
rgIncFee!!.visibility = View.VISIBLE
}
if (ctx.card!!.remainingSignatures < 2)
etAmount.isEnabled = false
tvCurrency.text = engine.balance.currency
etAmount.setText(engine.balance.toValueString())
// limit number of symbols after comma
etAmount.filters = engine.amountInputFilters
// set listeners
etAmount.setOnEditorActionListener { lv, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val imm = lv.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(lv.windowToken, 0)
lv.clearFocus()
true
} else {
false
}
}
btnVerify.setOnClickListener {
val engine1 = CoinEngineFactory.create(ctx)
val strAmount: String = etAmount.text.toString().replace(",", ".")
val amount = engine1!!.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString())
try {
if (!engine.checkNewTransactionAmount(amount))
etAmount.error = getString(R.string.not_enough_funds_on_your_card)
else
etAmount.error = null
} catch (e: Exception) {
etAmount.error = getString(R.string.unknown_amount_format)
}
var checkAddress = false
// if (engine1 != null)
checkAddress = engine1.validateAddress(etWallet.text.toString())
// check wallet address
if (!checkAddress) {
etWallet.error = getString(R.string.incorrect_destination_wallet_address)
return@setOnClickListener
} else {
etWallet.error = null
}
if (etWallet.text.toString() == ctx.coinData!!.wallet) {
etWallet.error = getString(R.string.destination_wallet_address_equal_source_address)
return@setOnClickListener
}
// check enough funds
// TODO - double with engin.checkAmount
// if (etAmount.text.toString().replace(",", ".").toDouble() > engine.getBalanceValue(card).replace(",", ".").toDouble()) {
// etAmount.error = getString(R.string.not_enough_funds_on_your_card)
// return@setOnClickListener
// }
if (!etAmount.error.isNullOrEmpty() || !etWallet.error.isNullOrEmpty()) {
return@setOnClickListener
}
val intent = Intent(baseContext, ConfirmPaymentActivity::class.java)
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString())
intent.putExtra(Constant.EXTRA_FEE_INCLUDED, (rgIncFee!!.checkedRadioButtonId == R.id.rbFeeIn))
intent.putExtra(Constant.EXTRA_AMOUNT, strAmount)
intent.putExtra(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString())
startActivityForResult(intent, Constant.REQUEST_CODE_SEND_PAYMENT__)
}
ivCamera.setOnClickListener { navigator.showQrScanActivity(this, Constant.REQUEST_CODE_SCAN_QR) }
}
public override fun onResume() {
super.onResume()
nfcManager!!.onResume()
}
public override fun onPause() {
super.onPause()
nfcManager!!.onPause()
}
public override fun onStop() {
super.onStop()
nfcManager!!.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constant.REQUEST_CODE_SCAN_QR && resultCode == Activity.RESULT_OK && data != null && data.extras!!.containsKey("QRCode")) {
var code = data.getStringExtra("QRCode")
when (ctx.blockchain) {
Blockchain.Bitcoin -> {
if (code.contains("bitcoin:")) {
val tmp = code.split("bitcoin:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
code = tmp[1]
}
}
Blockchain.Ethereum, Blockchain.Token -> {
if (code.contains("ethereum:")) {
val tmp = code.split("ethereum:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
code = tmp[1]
} else if (code.contains("blockchain:")) {
val tmp = code.split("blockchain:".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
code = tmp[1]
}
}
else -> {
}
}
etWallet!!.setText(code)
} else if (requestCode == Constant.REQUEST_CODE_SEND_PAYMENT__) {
setResult(resultCode, data)
finish()
}
}
override fun onTagDiscovered(tag: Tag) {
try {
nfcManager!!.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
}

View file

@ -1,199 +0,0 @@
package com.tangem.presentation.activity
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
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.view.View
import android.widget.Toast
import com.tangem.App
import com.tangem.tangemcard.tasks.PurgeTask
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.tangemcard.android.reader.NfcReader
import com.tangem.tangemcard.data.asBundle
import com.tangem.tangemcard.util.Util
import com.tangem.util.LOG
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_purge.*
class PurgeActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
val TAG: String = PurgeActivity::class.java.simpleName
const val RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER
fun callingIntent(context: Context, ctx: TangemContext): Intent {
val intent = Intent(context, PurgeActivity::class.java)
ctx.saveToIntent(intent)
return intent
}
}
private lateinit var ctx: TangemContext
private lateinit var nfcManager: NfcManager
private var purgeTask: PurgeTask? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_purge)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
nfcManager = NfcManager(this, this)
ctx = TangemContext.loadFromBundle(this, intent.extras)
tvCardID.text = ctx.card!!.cidDescription
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar.visibility = View.INVISIBLE
}
public override fun onResume() {
super.onResume()
nfcManager.onResume()
}
public override fun onPause() {
nfcManager.onPause()
purgeTask?.cancel(true)
super.onPause()
}
public override fun onStop() {
// dismiss enable NFC dialog
nfcManager.onStop()
purgeTask?.cancel(true)
super.onStop()
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag) ?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
LOG.d(TAG, "UID: $sUID")
if (sUID == ctx.card!!.uid) {
isoDep.timeout = ctx.card!!.pauseBeforePIN2 + 65000
purgeTask = PurgeTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
purgeTask!!.start()
} else {
LOG.d(TAG, "Mismatch card UID (" + sUID + " instead of " + ctx.card.uid + ")")
nfcManager.ignoreTag(isoDep.tag)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(this, msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this)
}
override fun onReadStart(cardProtocol: CardProtocol) {
progressBar.post {
progressBar.visibility = View.VISIBLE
progressBar.progress = 5
}
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
purgeTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.GREEN)
val intent = Intent()
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
setResult(Activity.RESULT_OK, intent)
finish()
}
} else {
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
val intent = Intent()
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
intent.putExtra("message", getString(R.string.cannot_erase_wallet))
setResult(RESULT_INVALID_PIN, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
} else {
progressBar!!.post {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed)
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
} else
Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_LONG).show()
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar!!.post { progressBar!!.progress = progress }
}
override fun onReadCancel() {
purgeTask = null
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
}

View file

@ -1,267 +0,0 @@
package com.tangem.presentation.activity
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.view.KeyEvent
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import com.tangem.App
import com.tangem.Constant
import com.tangem.domain.wallet.CoinEngine
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.tangemcard.android.reader.NfcReader
import com.tangem.tangemcard.data.asBundle
import com.tangem.tangemcard.tasks.SignTask
import com.tangem.tangemcard.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_sign_payment.*
class SignPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
val TAG: String = SignPaymentActivity::class.java.simpleName
}
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private var signPaymentTask: SignTask? = null
private lateinit var amount: CoinEngine.Amount
private lateinit var fee: CoinEngine.Amount
private var isIncludeFee = true
private var outAddressStr: String? = null
private var lastReadSuccess = true
private var progressBar: ProgressBar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sign_payment)
nfcManager = NfcManager(this, this)
ctx = TangemContext.loadFromBundle(this, intent.extras)
amount = CoinEngine.Amount(intent.getStringExtra(Constant.EXTRA_AMOUNT), intent.getStringExtra(Constant.EXTRA_AMOUNT_CURRENCY))
fee = CoinEngine.Amount(intent.getStringExtra(Constant.EXTRA_FEE), intent.getStringExtra(Constant.EXTRA_FEE_CURRENCY))
isIncludeFee = intent.getBooleanExtra(Constant.EXTRA_FEE_INCLUDED, true)
outAddressStr = intent.getStringExtra(Constant.EXTRA_TARGET_ADDRESS)
tvCardID.text = ctx.card!!.cidDescription
progressBar = findViewById(R.id.progressBar)
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
}
public override fun onResume() {
super.onResume()
nfcManager.onResume()
}
public override fun onPause() {
nfcManager.onPause()
if (signPaymentTask != null)
signPaymentTask!!.cancel(true)
super.onPause()
}
public override fun onStop() {
// dismiss enable NFC dialog
nfcManager.onStop()
if (signPaymentTask != null)
signPaymentTask!!.cancel(true)
super.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == Constant.REQUEST_CODE_SEND_PAYMENT_) {
setResult(resultCode, data)
finish()
return
}
super.onActivityResult(requestCode, resultCode, data)
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
val intent = Intent()
setResult(Activity.RESULT_CANCELED, intent)
finish()
return true
}
}
return super.onKeyDown(keyCode, event)
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
if (sUID == ctx.card!!.uid) {
if (lastReadSuccess) {
isoDep.timeout = ctx.card!!.pauseBeforePIN2 + 5000
} else {
isoDep.timeout = ctx.card!!.pauseBeforePIN2 + 65000
}
val coinEngine = CoinEngineFactory.create(ctx)
?: throw CardProtocol.TangemException("Can't create CoinEngine!")
coinEngine.setOnNeedSendPayment { tx ->
if (tx != null) {
val intent = Intent(this, SendTransactionActivity::class.java)
ctx.saveToIntent(intent)
intent.putExtra(Constant.EXTRA_TX, tx)
startActivityForResult(intent, Constant.REQUEST_CODE_SEND_PAYMENT_)
}
}
val paymentToSign = coinEngine.constructPayment(amount, fee, isIncludeFee, outAddressStr)
signPaymentTask = SignTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this, paymentToSign)
signPaymentTask!!.start()
} else
nfcManager.ignoreTag(isoDep.tag)
} catch (e: CardProtocol.TangemException_WrongAmount) {
try {
val intent = Intent()
intent.putExtra("message", getString(R.string.cannot_sign_transaction_wrong_amount))
intent.putExtra("UID", ctx.card.uid)
intent.putExtra("Card", ctx.card.asBundle)
setResult(Activity.RESULT_CANCELED, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onReadStart(cardProtocol: CardProtocol) {
progressBar!!.post {
progressBar!!.visibility = View.VISIBLE
progressBar!!.progress = 5
}
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar!!.post { progressBar!!.progress = progress }
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
signPaymentTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.GREEN)
}
} else {
lastReadSuccess = false
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
val intent = Intent()
intent.putExtra("message", getString(R.string.cannot_sign_transaction_make_sure_you_enter_correct_pin_2))
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
setResult(Constant.RESULT_INVALID_PIN_, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
} else {
if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) {
try {
val intent = Intent()
intent.putExtra("message", getString(R.string.cannot_sign_transaction_wrong_amount))
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
setResult(Activity.RESULT_CANCELED, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}
progressBar!!.post {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
NoExtendedLengthSupportDialog.message = getText(R.string.the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.the_nfc_adapter_length_apdu_advice).toString()
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
}
} else {
Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_LONG).show()
}
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadCancel() {
signPaymentTask = null
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(this, msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this)
}
}

View file

@ -1,168 +0,0 @@
package com.tangem.presentation.dialog;
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 com.tangem.wallet.R;
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);
}
}
});
}
}

View file

@ -1,132 +0,0 @@
package com.tangem.presentation.dialog
import android.app.Activity
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import android.support.v7.app.AppCompatDialogFragment
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.dialog_wait_pin2.*
import java.util.*
class WaitSecurityDelayDialogNew : AppCompatDialogFragment() {
companion object {
val TAG: String = WaitSecurityDelayDialogNew::class.java.simpleName
private const val MIN_REMAINING_DELAY_TO_SHOW_DIALOG = 1000
private const val DELAY_BEFORE_SHOW_DIALOG = 5000
}
private var msTimeout = 60000
private var msProgress = 0
private var timer: Timer? = null
private var timerToShowDelayDialog: Timer? = null
private var instance: WaitSecurityDelayDialogNew? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val inflater = activity!!.layoutInflater
val v = inflater.inflate(R.layout.dialog_wait_pin2, null)
progressBar.max = msTimeout
progressBar.progress = msProgress
timer = Timer()
timer!!.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
progressBar.post {
val progress = this@WaitSecurityDelayDialogNew.progressBar.progress
if (progress < this@WaitSecurityDelayDialogNew.progressBar.max) {
this@WaitSecurityDelayDialogNew.progressBar.progress = progress + 1000
}
}
}
}, 1000, 1000)
return AlertDialog.Builder(activity)
.setIcon(R.drawable.tangem_logo_small_new)
.setTitle(R.string.security_delay)
.setView(v)
.setCancelable(false)
.create()
}
fun onReadBeforeRequest(activity: Activity, timeout: Int) {
activity.runOnUiThread(Runnable {
if (timerToShowDelayDialog != null || timeout < DELAY_BEFORE_SHOW_DIALOG + MIN_REMAINING_DELAY_TO_SHOW_DIALOG)
return@Runnable
timerToShowDelayDialog = Timer()
timerToShowDelayDialog!!.schedule(object : TimerTask() {
override fun run() {
if (instance != null) return
instance = WaitSecurityDelayDialogNew()
instance!!.setup(timeout, DELAY_BEFORE_SHOW_DIALOG)
instance!!.isCancelable = false
// instance.show(activity.fragmentManager, TAG)
}
}, DELAY_BEFORE_SHOW_DIALOG.toLong())
})
}
fun onReadAfterRequest(activity: Activity) {
activity.runOnUiThread(Runnable {
if (timerToShowDelayDialog == null) return@Runnable
timerToShowDelayDialog!!.cancel()
timerToShowDelayDialog = null
})
}
fun onReadWait(activity: Activity, msec: Int) {
activity.runOnUiThread(Runnable {
if (timerToShowDelayDialog != null) {
timerToShowDelayDialog!!.cancel()
timerToShowDelayDialog = null
}
if (msec == 0) {
if (instance != null) {
instance!!.dismiss()
instance = null
}
return@Runnable
}
if (instance == null) {
if (msec > MIN_REMAINING_DELAY_TO_SHOW_DIALOG) {
instance = WaitSecurityDelayDialogNew()
// 1000ms - card delay notification interval
instance!!.setup(msec + 1000, 1000)
instance!!.isCancelable = false
// instance.show(activity.fragmentManager, TAG)
}
} else {
instance!!.setRemainingTimeout(msec)
}
})
}
private fun setup(msTimeout: Int, msProgress: Int) {
this.msTimeout = msTimeout
this.msProgress = msProgress
}
private fun setRemainingTimeout(msec: Int) {
progressBar.post {
val progress = this@WaitSecurityDelayDialogNew.progressBar.progress
if (timer != null) {
// we get delay latency from card for first time - don't change progress by timer, only by card answer
progressBar.max = progress + msec
timer!!.cancel()
timer = null
} else {
val newProgress = progressBar.max - msec
if (newProgress > progress)
progressBar.progress = newProgress
else
progressBar.max = progress + msec
}
}
}
}

View file

@ -1,34 +0,0 @@
/**
*
* 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.
*/
package com.tangem.presentation.viewmodel
import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel
import com.tangem.data.network.exception.Failure
/**
* Base ViewModel class with default Failure handling.
* @see ViewModel
* @see Failure
*/
abstract class BaseViewModel : ViewModel() {
var failure: MutableLiveData<Failure> = MutableLiveData()
protected fun handleFailure(failure: Failure) {
this.failure.value = failure
}
}

View file

@ -1,33 +0,0 @@
package com.tangem.presentation.viewmodel
import android.arch.lifecycle.MutableLiveData
import com.tangem.data.network.exception.Failure
class LoadedWalletViewModel : BaseViewModel() {
private val state: MutableLiveData<State> = MutableLiveData()
fun getState() = state
enum class State {
ServerError,
Failed,
Success
}
fun connectToken(data: String, data2: String) {
}
private fun handleTokensSaveFailure(failure: Failure) {
state.value = State.Success
handleFailure(failure)
}
private fun handleLoginFailure(failure: Failure) {
when (failure) {
is Failure.ServerError -> state.value = State.ServerError
}
handleFailure(failure)
}
}

View file

@ -0,0 +1,192 @@
package com.tangem.ui.activity
import android.app.Activity
import android.content.Context
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.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.wallet.TangemContext
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.card_android.android.nfc.NfcDeviceAntennaLocation
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.asBundle
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.CreateNewWalletTask
import com.tangem.card_common.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_create_new_wallet.*
import kotlinx.android.synthetic.main.layout_progress_horizontal.*
import kotlinx.android.synthetic.main.layout_touch_card.*
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
class CreateNewWalletActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
fun callingIntent(context: Context, ctx: TangemContext): Intent {
val intent = Intent(context, CreateNewWalletActivity::class.java)
intent.putExtra(EXTRA_TANGEM_CARD_UID, ctx.card!!.uid)
intent.putExtra(EXTRA_TANGEM_CARD, ctx.card!!.asBundle)
return intent
}
}
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
private var createNewWalletTask: CreateNewWalletTask? = null
private var lastReadSuccess = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create_new_wallet)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
ctx = TangemContext.loadFromBundle(this, intent.extras)
// init NFC Antenna
nfcDeviceAntenna = NfcDeviceAntennaLocation(this, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
nfcDeviceAntenna.init()
tvCardId.text = ctx.card!!.cidDescription
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar.visibility = View.INVISIBLE
}
override fun onTagDiscovered(tag: Tag) {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
if (sUID == ctx.card.uid) {
if (lastReadSuccess)
isoDep.timeout = ctx.card.pauseBeforePIN2 + 5000
else
isoDep.timeout = ctx.card.pauseBeforePIN2 + 65000
createNewWalletTask = CreateNewWalletTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
createNewWalletTask?.start()
} else
nfcManager.ignoreTag(isoDep.tag)
}
public override fun onPause() {
createNewWalletTask?.cancel(true)
super.onPause()
}
override fun onReadStart(cardProtocol: CardProtocol) {
rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE }
progressBar?.post {
progressBar?.visibility = View.VISIBLE
progressBar?.progress = 5
}
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
createNewWalletTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
rlProgressBar.post { rlProgressBar.visibility = View.GONE }
progressBar?.post {
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.GREEN)
val intent = Intent()
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
setResult(Activity.RESULT_OK, intent)
finish()
}
} else {
lastReadSuccess = false
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN) {
progressBar?.post {
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
}
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
val intent = Intent()
intent.putExtra(Constant.EXTRA_MESSAGE, getString(R.string.cannot_create_wallet))
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card!!.asBundle)
setResult(Constant.RESULT_INVALID_PIN, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
} else {
progressBar?.post {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
}
} else
Toast.makeText(this, R.string.try_to_scan_again, Toast.LENGTH_SHORT).show()
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
}
rlProgressBar.postDelayed({ rlProgressBar?.visibility = View.GONE }, 500)
progressBar?.postDelayed({
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
}, 500)
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar?.post { progressBar?.progress = progress }
}
override fun onReadCancel() {
createNewWalletTask = null
progressBar?.postDelayed({
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
}, 500)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(this, msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this)
}
}

View file

@ -0,0 +1,259 @@
package com.tangem.ui.activity
import android.app.Activity
import android.content.Context
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.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.text.Html
import android.view.View
import android.widget.Toast
import com.tangem.App
import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.wallet.TangemContext
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.asBundle
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.VerifyCardTask
import com.tangem.card_common.util.Util
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_empty_wallet.*
import kotlinx.android.synthetic.main.layout_tangem_card.*
import javax.inject.Inject
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.di.ToastHelper
class EmptyWalletActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
val TAG: String = EmptyWalletActivity::class.java.simpleName
fun callingIntent(context: Context, ctx: TangemContext): Intent {
val intent = Intent(context, EmptyWalletActivity::class.java)
ctx.saveToIntent(intent)
return intent
}
}
@Inject
internal lateinit var navigator: Navigator
@Inject
internal lateinit var toastHelper: ToastHelper
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private var lastReadSuccess = true
private var verifyCardTask: VerifyCardTask? = null
private var requestPIN2Count = 0
private var cardProtocol: CardProtocol? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_empty_wallet)
App.navigatorComponent.inject(this)
App.toastHelperComponent.inject(this)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
ctx = TangemContext.loadFromBundle(this, intent.extras)
tvIssuer.text = ctx.card!!.issuerDescription
if (ctx.card!!.tokenSymbol.length > 1) {
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(ctx.blockchainName, Html.FROM_HTML_MODE_LEGACY)
else
Html.fromHtml(ctx.blockchainName)
tvBlockchain.text = html
} else
tvBlockchain.text = ctx.blockchainName
tvCardID.text = ctx.card!!.cidDescription
ivTangemCard.setImageBitmap(App.localStorage.getCardArtworkBitmap(ctx.card))
// set listeners
btnNewWallet.setOnClickListener {
requestPIN2Count = 0
val intent = Intent(baseContext, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
intent.putExtra(EXTRA_TANGEM_CARD_UID, ctx.card!!.uid)
intent.putExtra(EXTRA_TANGEM_CARD, ctx.card!!.asBundle)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2)
}
btnDetails.setOnClickListener {
if (cardProtocol != null)
navigator.showVerifyCard(this, ctx)
else
toastHelper.showSingleToast(this, getString(R.string.need_attach_card_again))
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constant.REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY) {
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
data.putExtra(Constant.EXTRA_MODIFICATION, "updateAndViewCard")
data.putExtra("updateDelay", 0)
setResult(Activity.RESULT_OK, data)
}
finish()
} else {
if (data != null && data.extras!!.containsKey(EXTRA_TANGEM_CARD_UID) && data.extras!!.containsKey(EXTRA_TANGEM_CARD)) {
val updatedCard = TangemCard(data.getStringExtra(EXTRA_TANGEM_CARD_UID))
updatedCard.loadFromBundle(data.getBundleExtra(EXTRA_TANGEM_CARD))
ctx.card = updatedCard
}
if (resultCode == Constant.RESULT_INVALID_PIN && requestPIN2Count < 2) {
requestPIN2Count++
val intent = Intent(baseContext, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, PinRequestActivity.Mode.RequestPIN2.toString())
intent.putExtra(EXTRA_TANGEM_CARD_UID, ctx.card!!.uid)
intent.putExtra(EXTRA_TANGEM_CARD, ctx.card!!.asBundle)
startActivityForResult(intent, Constant.REQUEST_CODE_REQUEST_PIN2)
return
}
}
setResult(resultCode, data)
finish()
} else if (requestCode == Constant.REQUEST_CODE_REQUEST_PIN2) {
if (resultCode == Activity.RESULT_OK) {
navigator.showCreateNewWallet(this, ctx)
}
}
}
override fun onTagDiscovered(tag: Tag) {
try {
val isoDep = IsoDep.get(tag)
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
if (ctx.card.uid != sUID) {
nfcManager.ignoreTag(isoDep.tag)
return
}
if (lastReadSuccess)
isoDep.timeout = 1000
else
isoDep.timeout = 65000
verifyCardTask = VerifyCardTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, App.firmwaresStorage, this)
verifyCardTask?.start()
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onReadStart(cardProtocol: CardProtocol) {
rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE }
progressBar?.post {
progressBar?.visibility = View.VISIBLE
progressBar?.progress = 5
}
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
verifyCardTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
rlProgressBar?.post { rlProgressBar.visibility = View.GONE }
progressBar?.post {
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.GREEN)
this.cardProtocol = cardProtocol
}
} else {
// remove last UIDs because of error and no card read
progressBar?.post {
lastReadSuccess = false
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed)
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
} else
Toast.makeText(this@EmptyWalletActivity, R.string.try_to_scan_again, Toast.LENGTH_LONG).show()
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
rlProgressBar?.postDelayed({
try {
rlProgressBar?.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar?.post { progressBar!!.progress = progress }
}
override fun onReadCancel() {
verifyCardTask = null
rlProgressBar?.postDelayed({
try {
rlProgressBar?.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(this, msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this)
}
}

View file

@ -1,16 +1,17 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.content.Context
import android.content.Intent
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.fragment.LoadedWallet
import com.tangem.di.ToastHelper
import com.tangem.wallet.TangemContext
import com.tangem.ui.fragment.LoadedWalletFragment
import com.tangem.wallet.R
import javax.inject.Inject
@ -18,6 +19,8 @@ class LoadedWalletActivity : AppCompatActivity() {
@Inject
lateinit var navigator: Navigator
@Inject
internal lateinit var toastHelper: ToastHelper
companion object {
fun callingIntent(context: Context, lastTag: Tag, ctx: TangemContext): Intent {
@ -32,12 +35,13 @@ class LoadedWalletActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_loaded_wallet)
App.getNavigatorComponent().inject(this)
App.navigatorComponent.inject(this)
App.toastHelperComponent.inject(this)
if (intent.extras!!.containsKey(NfcAdapter.EXTRA_TAG)) {
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag != null) {
val fragment = supportFragmentManager.findFragmentById(R.id.loaded_wallet_fragment) as LoadedWallet
val fragment = supportFragmentManager.findFragmentById(R.id.loaded_wallet_fragment) as LoadedWalletFragment
fragment.onTagDiscovered(tag)
}
}

View file

@ -1,22 +1,21 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.wallet.TangemContext
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_logo.*
import javax.inject.Inject
class LogoActivity : AppCompatActivity() {
companion object {
fun callingIntent(context: Context, autoHide: Boolean): Intent {
val intent = Intent(context, LogoActivity::class.java)
intent.putExtra(Constant.EXTRA_AUTO_HIDE, autoHide)
@ -24,35 +23,54 @@ class LogoActivity : AppCompatActivity() {
}
}
private val hideRunnable = Runnable { this.hide() }
@Inject
internal lateinit var navigator: Navigator
private val hideRunnable = Runnable { this.hide() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_logo)
App.getNavigatorComponent().inject(this)
App.navigatorComponent.inject(this)
ivLogo.setOnClickListener { hide() }
}
@SuppressLint("SetTextI18n")
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// set beta version name
if (BuildConfig.DEBUG)
tvAppVersion.text = "BETA v." + BuildConfig.VERSION_NAME + "\n" + "dev" + "\n" + "build " + BuildConfig.VERSION_CODE
tvAppVersion.text = String.format(getString(R.string.version_name_debug), BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
else
tvAppVersion.text = "BETA v." + BuildConfig.VERSION_NAME
tvAppVersion.text = String.format(getString(R.string.version_name_release), BuildConfig.VERSION_NAME)
// set flavor app name
when (BuildConfig.FLAVOR) {
Constant.FLAVOR_TANGEM_CARDANO -> {
tvExtension.visibility = View.VISIBLE
tvExtension.text = getString(R.string.cardano)
}
else -> {
tvExtension.visibility = View.GONE
}
}
if (intent.getBooleanExtra(Constant.EXTRA_AUTO_HIDE, true))
ivLogo.postDelayed(hideRunnable, Constant.MILLIS_AUTO_HIDE.toLong())
}
private fun hide() {
navigator.showMain(this)
when (BuildConfig.FLAVOR) {
Constant.FLAVOR_TANGEM_CARDANO -> {
navigator.showPrepareTransaction(this, TangemContext())
}
else -> {
navigator.showMain(this)
}
}
finish()
}

View file

@ -0,0 +1,88 @@
@file:Suppress("ObsoleteExperimentalCoroutines")
package com.tangem.ui.activity
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.navigation.NavController
import androidx.navigation.Navigation
import com.scottyab.rootbeer.RootBeer
import com.tangem.App
import com.tangem.Constant
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.di.Navigator
import com.tangem.di.ToastHelper
import com.tangem.ui.dialog.RootFoundDialog
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
companion object {
val TAG: String = MainActivity::class.java.simpleName
fun callingIntent(context: Context) = Intent(context, MainActivity::class.java)
}
lateinit var navController: NavController
@Inject
internal lateinit var navigator: Navigator
@Inject
internal lateinit var toastHelper: ToastHelper
// private var onNfcReaderCallback: NfcAdapter.ReaderCallback? = null
// override fun onNewIntent(intent: Intent?) {
// super.onNewIntent(intent)
// if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
// val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
// if (tag != null && onNfcReaderCallback != null)
// onNfcReaderCallback?.onTagDiscovered(tag)
// }
// }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navController = Navigation.findNavController(this, R.id.nav_host_fragment)
App.navigatorComponent.inject(this)
App.toastHelperComponent.inject(this)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
verifyPermissions()
// // NFC
// val intent = intent
// if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action || NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action)) {
// val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
// if (tag != null && onNfcReaderCallback != null) {
// onNfcReaderCallback?.onTagDiscovered(tag)
// }
// }
// check if root device
val rootBeer = RootBeer(this)
if (rootBeer.isRootedWithoutBusyBoxCheck && !BuildConfig.DEBUG)
RootFoundDialog().show(supportFragmentManager, RootFoundDialog.TAG)
}
private fun verifyPermissions() {
NfcManager.verifyPermissions(this)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), Constant.REQUEST_CODE_REQUEST_CAMERA_PERMISSIONS)
}
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.Manifest
import android.annotation.SuppressLint
@ -13,30 +13,33 @@ import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Build
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.appcompat.app.AppCompatActivity
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.widget.Button
import com.tangem.Constant
import com.tangem.data.fingerprint.StartFingerprintReaderTask
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.data.fingerprint.FingerprintHelper
import com.tangem.domain.wallet.TangemContext
import com.tangem.tangemcard.android.data.PINStorage
import com.tangem.tangemcard.data.TangemCard
import com.tangem.tangemcard.data.loadFromBundle
import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD
import com.tangem.tangemcard.data.EXTRA_TANGEM_CARD_UID
import com.tangem.wallet.TangemContext
import com.tangem.card_android.android.data.PINStorage
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.data.loadFromBundle
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
import com.tangem.card_common.data.TangemCard
import com.tangem.util.LOG
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_pin_request.*
import kotlinx.android.synthetic.main.layout_pin_buttons.*
import java.io.IOException
class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, FingerprintHelper.FingerprintHelperListener {
companion object {
val TAG: String = PinRequestActivity::class.java.simpleName
fun callingIntent(context: Activity, mode: String): Intent {
val intent = Intent(context, PinRequestActivity::class.java)
intent.putExtra(Constant.EXTRA_MODE, mode)
@ -81,12 +84,11 @@ class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Finge
}
}
private lateinit var nfcManager: NfcManager
lateinit var mode: Mode
private var allowFingerprint = false
private var nfcManager: NfcManager? = null
var startFingerprintReaderTask: StartFingerprintReaderTask? = null
private var fingerprintManager: FingerprintManager? = null
private var fingerprintHelper: FingerprintHelper? = null
@ -99,6 +101,7 @@ class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Finge
setContentView(R.layout.activity_pin_request)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
mode = Mode.valueOf(intent.getStringExtra(Constant.EXTRA_MODE))
@ -170,34 +173,26 @@ class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Finge
override fun onPause() {
super.onPause()
if (fingerprintHelper != null)
fingerprintHelper!!.cancel()
fingerprintHelper?.cancel()
if (startFingerprintReaderTask != null) {
startFingerprintReaderTask!!.cancel(true)
startFingerprintReaderTask = null
}
nfcManager!!.onPause()
}
override fun onStop() {
super.onStop()
if (fingerprintHelper != null)
fingerprintHelper!!.cancel()
fingerprintHelper?.cancel()
if (startFingerprintReaderTask != null) {
startFingerprintReaderTask!!.cancel(true)
startFingerprintReaderTask = null
}
nfcManager!!.onStop()
}
override fun onResume() {
super.onResume()
nfcManager!!.onResume()
if (allowFingerprint)
startFingerprintReader()
}
@ -205,33 +200,33 @@ class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Finge
override fun onTagDiscovered(tag: Tag) {
try {
Log.w(javaClass.name, "Ignore discovered tag!")
nfcManager!!.ignoreTag(tag)
nfcManager.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
override fun authenticationFailed(error: String) {
doLog(error)
LOG.w(TAG, error)
}
@TargetApi(Build.VERSION_CODES.M)
override fun authenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
doLog("Authentication succeeded!")
LOG.i(TAG, "Authentication succeeded!")
val cipher = result.cryptoObject.cipher
if (mode == Mode.RequestNewPIN || mode == Mode.ConfirmNewPIN) {
val resultData = Intent()
val pin = PINStorage.loadEncryptedPIN(cipher)
resultData.putExtra("newPIN", pin)
resultData.putExtra("confirmPIN", pin)
resultData.putExtra(Constant.EXTRA_NEW_PIN, pin)
resultData.putExtra(Constant.EXTRA_CONFIRM_PIN, pin)
setResult(Activity.RESULT_OK, resultData)
finish()
} else if (mode == Mode.RequestNewPIN2 || mode == Mode.ConfirmNewPIN2) {
val resultData = Intent()
val pin = PINStorage.loadEncryptedPIN2(cipher)
resultData.putExtra("newPIN2", pin)
resultData.putExtra("confirmPIN2", pin)
resultData.putExtra(Constant.EXTRA_NEW_PIN_2, pin)
resultData.putExtra(Constant.EXTRA_CONFIRM_PIN_2, pin)
setResult(Activity.RESULT_OK, resultData)
finish()
} else if (mode == Mode.RequestPIN) {
@ -245,38 +240,33 @@ class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Finge
finish()
}
fun doLog(text: String) {
// Log.e("FP", text);
}
@SuppressLint("SetTextI18n")
private fun buttonClick(button: Button) {
tvPin!!.text = tvPin!!.text.toString() + button.text as String
tvPin.text = tvPin.text.toString() + button.text.toString()
}
@SuppressLint("NewApi")
private fun testFingerPrintSettings(): Boolean {
doLog("Testing Fingerprint Settings")
LOG.i(TAG, "Testing Fingerprint SettingsFragment")
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
fingerprintManager = getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager
if (!keyguardManager.isKeyguardSecure) {
doLog("User hasn't enabled Lock Screen")
LOG.i(TAG, "User hasn't enabled Lock Screen")
return false
}
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
doLog("User hasn't granted permission to use Fingerprint")
LOG.i(TAG, "User hasn't granted permission to use Fingerprint")
return false
}
if (!fingerprintManager!!.hasEnrolledFingerprints()) {
doLog("User hasn't registered any fingerprints")
LOG.i(TAG, "User hasn't registered any fingerprints")
return false
}
doLog("Fingerprint authentication is set.\n")
LOG.i(TAG, "Fingerprint authentication is set.\n")
return true
}
@ -307,13 +297,13 @@ class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Finge
var focusView: View? = null
if (mode == Mode.ConfirmNewPIN) {
if (pin != intent.getStringExtra("newPIN")) {
if (pin != intent.getStringExtra(Constant.EXTRA_NEW_PIN)) {
tvPin!!.error = getString(R.string.error_pin_confirmation_failed)
focusView = tvPin
cancel = true
}
} else if (mode == Mode.ConfirmNewPIN2) {
if (pin != intent.getStringExtra("newPIN2")) {
if (pin != intent.getStringExtra(Constant.EXTRA_NEW_PIN_2)) {
tvPin!!.error = getString(R.string.error_pin_confirmation_failed)
focusView = tvPin
cancel = true
@ -331,17 +321,17 @@ class PinRequestActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Finge
else {
if (mode == Mode.RequestNewPIN || mode == Mode.ConfirmNewPIN) {
val resultData = Intent()
resultData.putExtra("newPIN", pin)
resultData.putExtra(Constant.EXTRA_NEW_PIN, pin)
if (mode == Mode.ConfirmNewPIN)
resultData.putExtra("confirmPIN", pin)
resultData.putExtra(Constant.EXTRA_CONFIRM_PIN, pin)
setResult(Activity.RESULT_OK, resultData)
finish()
} else if (mode == Mode.RequestNewPIN2 || mode == Mode.ConfirmNewPIN2) {
val resultData = Intent()
resultData.putExtra("newPIN2", pin)
resultData.putExtra(Constant.EXTRA_NEW_PIN_2, pin)
if (mode == Mode.ConfirmNewPIN2)
resultData.putExtra("confirmPIN2", pin)
resultData.putExtra(Constant.EXTRA_CONFIRM_PIN_2, pin)
setResult(Activity.RESULT_OK, resultData)
finish()

View file

@ -1,4 +1,4 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.Manifest
import android.annotation.SuppressLint
@ -14,8 +14,8 @@ import android.os.Bundle
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyPermanentlyInvalidatedException
import android.security.keystore.KeyProperties
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.appcompat.app.AppCompatActivity
import android.text.TextUtils
import android.view.View
import android.widget.Button
@ -23,7 +23,7 @@ import android.widget.Toast
import com.tangem.Constant
import com.tangem.data.fingerprint.ConfirmWithFingerprintTask
import com.tangem.data.fingerprint.FingerprintHelper
import com.tangem.tangemcard.android.data.PINStorage
import com.tangem.card_android.android.data.PINStorage
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_pin_save.*
import kotlinx.android.synthetic.main.layout_pin_buttons.*
@ -39,7 +39,6 @@ import javax.crypto.SecretKey
import javax.crypto.spec.IvParameterSpec
class PinSaveActivity : AppCompatActivity(), FingerprintHelper.FingerprintHelperListener {
companion object {
val TAG: String = PinSaveActivity::class.java.simpleName

View file

@ -1,4 +1,4 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.app.Activity
import android.content.Context
@ -9,26 +9,29 @@ import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.IsoDep
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import com.tangem.App
import com.tangem.Constant
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.tangemcard.android.reader.NfcReader
import com.tangem.tangemcard.data.*
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.tasks.SwapPINTask
import com.tangem.tangemcard.util.Util
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.*
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.SwapPINTask
import com.tangem.card_common.util.Util
import com.tangem.util.LOG
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_pin_swap.*
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
class PinSwapActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
val TAG: String = PinSwapActivity::class.java.simpleName
@ -43,8 +46,8 @@ class PinSwapActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProt
}
private lateinit var nfcManager: NfcManager
private var card: TangemCard? = null
private lateinit var card: TangemCard;
private var newPIN: String? = null
private var newPIN2: String? = null
@ -57,35 +60,33 @@ class PinSwapActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProt
setContentView(R.layout.activity_pin_swap)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
card = TangemCard(intent.getStringExtra(EXTRA_TANGEM_CARD_UID))
card!!.loadFromBundle(intent.extras!!.getBundle(EXTRA_TANGEM_CARD))
card.loadFromBundle(intent.extras!!.getBundle(EXTRA_TANGEM_CARD))
newPIN = intent.getStringExtra(Constant.EXTRA_NEW_PIN)
newPIN2 = intent.getStringExtra(Constant.EXTRA_NEW_PIN_2)
tvCardID.text = card!!.cidDescription
tvCardID.text = card.cidDescription
progressBar = findViewById(R.id.progressBar)
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
?: throw CardProtocol.TangemException(getString(R.string.wrong_tag_err))
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
LOG.d(TAG, "UID: $sUID")
if (sUID == card!!.uid) {
isoDep.timeout = card!!.pauseBeforePIN2 + 65000
if (sUID == card.uid) {
isoDep.timeout = card.pauseBeforePIN2 + 65000
swapPinTask = SwapPINTask(card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this, newPIN, newPIN2)
swapPinTask!!.start()
swapPinTask?.start()
} else {
LOG.d(TAG, "Mismatch card UID (" + sUID + " instead of " + card!!.uid + ")")
nfcManager.ignoreTag(isoDep.tag)
}
} catch (e: Exception) {
@ -93,23 +94,13 @@ class PinSwapActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProt
}
}
public override fun onResume() {
super.onResume()
nfcManager.onResume()
}
public override fun onPause() {
nfcManager.onPause()
if (swapPinTask != null)
swapPinTask!!.cancel(true)
swapPinTask?.cancel(true)
super.onPause()
}
public override fun onStop() {
// dismiss enable NFC dialog
nfcManager.onStop()
if (swapPinTask != null)
swapPinTask!!.cancel(true)
swapPinTask?.cancel(true)
super.onStop()
}
@ -129,8 +120,8 @@ class PinSwapActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProt
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.GREEN)
val intent = Intent()
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
setResult(Activity.RESULT_OK, intent)
finish()
}
@ -145,9 +136,9 @@ class PinSwapActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProt
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
val intent = Intent()
intent.putExtra("message", "Cannot change PIN(s). Make sure you enter correct PIN2!")
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
intent.putExtra(Constant.EXTRA_MESSAGE, "Cannot change PIN(s). Make sure you enter correct PIN2!")
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
setResult(RESULT_INVALID_PIN, intent)
finish()
} catch (e: Exception) {
@ -168,11 +159,11 @@ class PinSwapActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProt
}
}
progressBar!!.postDelayed({
progressBar?.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}

View file

@ -1,4 +1,4 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.annotation.SuppressLint
import android.app.Activity
@ -7,16 +7,17 @@ import android.graphics.Color
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import android.view.View
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.Blockchain
import com.tangem.data.network.CryptonitOtherApi
import com.tangem.di.Navigator
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_prepare_cryptonit_other_api_withdrawal.*
import java.io.IOException
@ -28,8 +29,9 @@ class PrepareCryptonitOtherApiWithdrawalActivity : AppCompatActivity(), NfcAdapt
val TAG: String = PrepareCryptonitOtherApiWithdrawalActivity::class.java.simpleName
}
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private var nfcManager: NfcManager? = null
private var cryptonit: CryptonitOtherApi? = null
@Inject
@ -40,9 +42,10 @@ class PrepareCryptonitOtherApiWithdrawalActivity : AppCompatActivity(), NfcAdapt
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_prepare_cryptonit_other_api_withdrawal)
App.getNavigatorComponent().inject(this)
App.navigatorComponent.inject(this)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
ctx = TangemContext.loadFromBundle(this, intent.extras)
@ -128,47 +131,6 @@ class PrepareCryptonitOtherApiWithdrawalActivity : AppCompatActivity(), NfcAdapt
doRequestBalance()
}
private fun doRequestBalance() {
if (cryptonit!!.havaAccountInfo()) {
rlProgressBar.visibility = View.VISIBLE
tvProgressDescription.text = getString(R.string.cryptonit_request_balance)
tvError.visibility = View.INVISIBLE
cryptonit!!.requestBalance(ctx.blockchain.currency, "USD")
} else {
tvError.visibility = View.VISIBLE
tvError.text = getString(R.string.cryptonit_not_enough_account_data)
}
}
// private fun EditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
// this.addTextChangedListener(object : TextWatcher {
// override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
// }
//
// override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
// }
//
// override fun afterTextChanged(editable: Editable?) {
// afterTextChanged.invoke(editable.toString())
// }
// })
// }
public override fun onResume() {
super.onResume()
nfcManager!!.onResume()
}
public override fun onPause() {
super.onPause()
nfcManager!!.onPause()
}
public override fun onStop() {
super.onStop()
nfcManager!!.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && data != null && data.extras!!.containsKey("QRCode")) {
@ -193,12 +155,22 @@ class PrepareCryptonitOtherApiWithdrawalActivity : AppCompatActivity(), NfcAdapt
override fun onTagDiscovered(tag: Tag) {
try {
// Log.w(javaClass.name, "Ignore discovered tag!")
nfcManager!!.ignoreTag(tag)
nfcManager.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
private fun doRequestBalance() {
if (cryptonit!!.havaAccountInfo()) {
rlProgressBar.visibility = View.VISIBLE
tvProgressDescription.text = getString(R.string.cryptonit_request_balance)
tvError.visibility = View.INVISIBLE
cryptonit!!.requestBalance(ctx.blockchain.currency, "USD")
} else {
tvError.visibility = View.VISIBLE
tvError.text = getString(R.string.cryptonit_not_enough_account_data)
}
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.annotation.SuppressLint
import android.content.Context
@ -6,17 +6,18 @@ import android.graphics.Color
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import android.text.InputFilter
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import com.tangem.data.network.Cryptonit
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.data.Blockchain
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.util.DecimalDigitsInputFilter
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_prepare_cryptonit_withdrawal.*
@ -29,19 +30,20 @@ class PrepareCryptonitWithdrawalActivity : AppCompatActivity(), NfcAdapter.Reade
}
private lateinit var ctx: TangemContext
private var nfcManager: NfcManager? = null
private var cryptonit: Cryptonit? = null
private lateinit var nfcManager: NfcManager
private var cryptonit: Cryptonit? = null
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_prepare_cryptonit_withdrawal)
nfcManager = NfcManager(this, this)
ctx = TangemContext.loadFromBundle(this, intent.extras)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
cryptonit = Cryptonit(this)
etUsername.setText(cryptonit!!.username)
@ -56,17 +58,17 @@ class PrepareCryptonitWithdrawalActivity : AppCompatActivity(), NfcAdapter.Reade
tvFeeCurrency.text = engine.feeCurrency
etAmount.setText(engine.convertToAmount(engine.convertToInternalAmount(ctx.card!!.denomination)).toValueString())
etAmount.filters=engine.amountInputFilters
etAmount.filters = engine.amountInputFilters
etAmount.setOnEditorActionListener { lv, actionId, event ->
etAmount.setOnEditorActionListener { lv, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
val imm = lv.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(lv.windowToken, 0)
true
} else {
} else
false
}
}
when (ctx.blockchain) {
Blockchain.Bitcoin -> {
etAmount.filters = arrayOf<InputFilter>(DecimalDigitsInputFilter(5))
@ -90,7 +92,6 @@ class PrepareCryptonitWithdrawalActivity : AppCompatActivity(), NfcAdapter.Reade
val strAmount: String = etAmount.text.toString().replace(",", ".")
val strFee: String = etFee.text.toString().replace(",", ".")
val dblAmount: Double = strAmount.toDouble()
var dblFee: Double = strFee.toDouble()
cryptonit!!.fee = strFee
rlProgressBar.visibility = View.VISIBLE
@ -125,7 +126,7 @@ class PrepareCryptonitWithdrawalActivity : AppCompatActivity(), NfcAdapter.Reade
cryptonit!!.setWithdrawalListener { response ->
rlProgressBar.visibility = View.INVISIBLE
if (response.success != null && response.success!!) {
Toast.makeText(this, "Withdrawal successful!", Toast.LENGTH_LONG).show();
Toast.makeText(this, R.string.withdrawal_successful, Toast.LENGTH_LONG).show();
finish()
} else {
tvError.visibility = View.VISIBLE
@ -136,6 +137,14 @@ class PrepareCryptonitWithdrawalActivity : AppCompatActivity(), NfcAdapter.Reade
doRequestBalance()
}
override fun onTagDiscovered(tag: Tag) {
try {
nfcManager.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
private fun doRequestBalance() {
if (cryptonit!!.haveAccountInfo()) {
rlProgressBar.visibility = View.VISIBLE
@ -148,28 +157,4 @@ class PrepareCryptonitWithdrawalActivity : AppCompatActivity(), NfcAdapter.Reade
}
}
public override fun onResume() {
super.onResume()
nfcManager!!.onResume()
}
public override fun onPause() {
super.onPause()
nfcManager!!.onPause()
}
public override fun onStop() {
super.onStop()
nfcManager!!.onStop()
}
override fun onTagDiscovered(tag: Tag) {
try {
nfcManager!!.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.annotation.SuppressLint
import android.app.Activity
@ -10,7 +10,7 @@ import android.graphics.Color
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
@ -18,11 +18,12 @@ import android.widget.Toast
import com.tangem.App
import com.tangem.Constant
import com.tangem.data.network.Kraken
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.data.Blockchain
import com.tangem.di.Navigator
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_prepare_kraken_withdrawal.*
import java.io.IOException
@ -38,7 +39,8 @@ class PrepareKrakenWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCa
}
private lateinit var ctx: TangemContext
private var nfcManager: NfcManager? = null
private lateinit var nfcManager: NfcManager
private var kraken: Kraken? = null
private var fee: BigDecimal? = null
@ -50,12 +52,13 @@ class PrepareKrakenWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCa
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_prepare_kraken_withdrawal)
App.getNavigatorComponent().inject(this)
nfcManager = NfcManager(this, this)
App.navigatorComponent.inject(this)
ctx = TangemContext.loadFromBundle(this, intent.extras)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
kraken = Kraken(this)
tvKey.text = kraken!!.key
@ -68,7 +71,7 @@ class PrepareKrakenWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCa
tvCurrency.text = engine!!.balanceCurrency
etAmount.setText(engine.convertToAmount(engine.convertToInternalAmount(ctx.card!!.denomination)).toValueString())
etAmount.filters=engine.amountInputFilters
etAmount.filters = engine.amountInputFilters
etAmount.setOnEditorActionListener { lv, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
@ -167,7 +170,7 @@ class PrepareKrakenWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCa
val builder = AlertDialog.Builder(this)
// Set a title for alert dialog
builder.setTitle("Please confirm withdraw")
builder.setTitle(R.string.please_confirm_withdraw)
// Set a message for alert dialog
builder.setMessage(String.format("Continue with fee %s %s?", fee!!.toString().trimEnd('0'), ctx.blockchain.currency))
@ -186,23 +189,22 @@ class PrepareKrakenWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCa
rlProgressBar.visibility = View.VISIBLE
tvProgressDescription.text = getString(R.string.kraken_request_withdrawal)
//Toast.makeText(this, String.format("Withdraw %s!",dblAmount.toString()), Toast.LENGTH_LONG).show()
kraken!!.requestWithdraw(ctx.blockchain.currency, dblAmount.toString(), ctx.coinData!!.wallet)
} catch (e: Exception) {
etAmount.error = getString(R.string.unknown_amount_format)
}
}
DialogInterface.BUTTON_NEGATIVE -> {
Toast.makeText(this, "Operation canceled!", Toast.LENGTH_LONG).show()
Toast.makeText(this, R.string.operation_canceled, Toast.LENGTH_LONG).show()
}
}
}
// Set the alert dialog positive/yes button
builder.setPositiveButton("YES", dialogClickListener)
builder.setPositiveButton(R.string.yes, dialogClickListener)
// Set the alert dialog negative/no button
builder.setNegativeButton("NO", dialogClickListener)
builder.setNegativeButton(R.string.no, dialogClickListener)
// Initialize the AlertDialog using builder object
@ -224,21 +226,6 @@ class PrepareKrakenWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCa
}
}
public override fun onResume() {
super.onResume()
nfcManager!!.onResume()
}
public override fun onPause() {
super.onPause()
nfcManager!!.onPause()
}
public override fun onStop() {
super.onStop()
nfcManager!!.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && data != null && data.extras!!.containsKey("QRCode")) {
@ -262,12 +249,10 @@ class PrepareKrakenWithdrawalActivity : AppCompatActivity(), NfcAdapter.ReaderCa
override fun onTagDiscovered(tag: Tag) {
try {
// Log.w(javaClass.name, "Ignore discovered tag!")
nfcManager!!.ignoreTag(tag)
nfcManager.ignoreTag(tag)
} catch (e: IOException) {
e.printStackTrace()
}
}
}

View file

@ -0,0 +1,236 @@
package com.tangem.ui.activity
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
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.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.wallet.TangemContext
import com.tangem.ui.dialog.NoExtendedLengthSupportDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialog
import com.tangem.ui.dialog.WaitSecurityDelayDialogNew
import com.tangem.ui.event.DeletingWalletFinish
import com.tangem.card_android.android.nfc.NfcDeviceAntennaLocation
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_android.android.reader.NfcReader
import com.tangem.card_android.data.asBundle
import com.tangem.card_common.reader.CardProtocol
import com.tangem.card_common.tasks.PurgeTask
import com.tangem.card_common.util.Util
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_purge.*
import kotlinx.android.synthetic.main.layout_touch_card.*
import org.greenrobot.eventbus.EventBus
import javax.inject.Inject
import com.tangem.card_android.data.EXTRA_TANGEM_CARD
import com.tangem.card_android.data.EXTRA_TANGEM_CARD_UID
class PurgeActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object {
val TAG: String = PurgeActivity::class.java.simpleName
fun callingIntent(context: Context, ctx: TangemContext): Intent {
val intent = Intent(context, PurgeActivity::class.java)
ctx.saveToIntent(intent)
return intent
}
const val RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER
}
@Inject
internal lateinit var waitSecurityDelayDialogNew: WaitSecurityDelayDialogNew
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation
private var purgeTask: PurgeTask? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_purge)
App.navigatorComponent.inject(this)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
ctx = TangemContext.loadFromBundle(this, intent.extras)
// init NFC Antenna
nfcDeviceAntenna = NfcDeviceAntennaLocation(this, ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc)
nfcDeviceAntenna.init()
tvCardID.text = ctx.card.cidDescription
progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar.visibility = View.INVISIBLE
}
public override fun onStop() {
purgeTask?.cancel(true)
super.onStop()
}
override fun onTagDiscovered(tag: Tag) {
try {
// get IsoDep handle and run cardReader thread
val isoDep = IsoDep.get(tag)
val uid = tag.id
val sUID = Util.byteArrayToHexString(uid)
if (sUID == ctx.card.uid) {
isoDep.timeout = ctx.card.pauseBeforePIN2 + 65000
purgeTask = PurgeTask(ctx.card, NfcReader(nfcManager, isoDep), App.localStorage, App.pinStorage, this)
purgeTask?.start()
} else {
nfcManager.ignoreTag(isoDep.tag)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.onReadWait(this, msec)
// val readWait = ReadWait()
// readWait.msec = msec
// EventBus.getDefault().post(readWait)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
// if (!waitSecurityDelayDialogNew.isAdded)
// waitSecurityDelayDialogNew.show(supportFragmentManager, WaitSecurityDelayDialogNew.TAG)
//
//
// val readBeforeRequest = ReadBeforeRequest()
// readBeforeRequest.timeout = timeout
// EventBus.getDefault().post(readBeforeRequest)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this)
// val readAfterRequest = ReadAfterRequest()
// EventBus.getDefault().post(readAfterRequest)
}
override fun onReadStart(cardProtocol: CardProtocol) {
rlProgressBar.post { rlProgressBar.visibility = View.VISIBLE }
progressBar.post {
progressBar.visibility = View.VISIBLE
progressBar.progress = 5
}
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
purgeTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
rlProgressBar.post { rlProgressBar.visibility = View.GONE }
progressBar?.post {
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.GREEN)
val intent = Intent()
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
setResult(Activity.RESULT_OK, intent)
EventBus.getDefault().post(DeletingWalletFinish())
finish()
}
} else {
if (cardProtocol.error is CardProtocol.TangemException_InvalidPIN) {
progressBar?.post {
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
}
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
val intent = Intent()
intent.putExtra(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid)
intent.putExtra(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle)
intent.putExtra(Constant.EXTRA_MESSAGE, getString(R.string.cannot_erase_wallet))
setResult(RESULT_INVALID_PIN, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
} else {
progressBar?.post {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed)
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
} else
Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_LONG).show()
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
}
rlProgressBar?.postDelayed({
try {
rlProgressBar.visibility = View.GONE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar?.post { progressBar?.progress = progress }
}
override fun onReadCancel() {
purgeTask = null
progressBar?.postDelayed({
try {
progressBar?.progress = 0
progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar?.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.Manifest
import android.app.Activity
@ -6,14 +6,13 @@ import android.content.Context
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 androidx.core.app.ActivityCompat
import androidx.appcompat.app.AppCompatActivity
import com.google.zxing.Result
import com.tangem.Constant
import me.dm7.barcodescanner.zxing.ZXingScannerView
class QrScanActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
companion object {
fun callingIntent(context: Context): Intent {
return Intent(context, QrScanActivity::class.java)

View file

@ -1,19 +1,22 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.content.Intent
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import android.view.KeyEvent
import android.widget.Toast
import com.tangem.App
import com.tangem.Constant
import com.tangem.domain.wallet.CoinEngine
import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.event.TransactionFinishWithError
import com.tangem.presentation.event.TransactionFinishWithSuccess
import com.tangem.tangemcard.android.reader.NfcManager
import com.tangem.wallet.CoinEngine
import com.tangem.wallet.CoinEngineFactory
import com.tangem.wallet.TangemContext
import com.tangem.ui.event.TransactionFinishWithError
import com.tangem.ui.event.TransactionFinishWithSuccess
import com.tangem.card_android.android.nfc.NfcLifecycleObserver
import com.tangem.card_android.android.reader.NfcManager
import com.tangem.card_common.util.Util
import com.tangem.util.UtilHelper
import com.tangem.wallet.R
import org.greenrobot.eventbus.EventBus
@ -21,15 +24,17 @@ import java.io.IOException
class SendTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
private lateinit var ctx: TangemContext
private var tx: ByteArray? = null
private lateinit var nfcManager: NfcManager
private lateinit var ctx: TangemContext
private var tx: ByteArray? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_send_transaction)
nfcManager = NfcManager(this, this)
lifecycle.addObserver(NfcLifecycleObserver(nfcManager))
ctx = TangemContext.loadFromBundle(this, intent.extras)
tx = intent.getByteArrayExtra(Constant.EXTRA_TX)
@ -39,9 +44,10 @@ class SendTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
engine!!.requestSendTransaction(
object : CoinEngine.BlockchainRequestsCallbacks {
override fun onComplete(success: Boolean) {
if (success)
if (success) {
App.pendingTransactionsStorage.putTransaction(ctx.card, Util.bytesToHex(tx), engine.pendingTransactionTimeoutInSeconds())
finishWithSuccess()
else
}else
finishWithError(ctx.error)
}
@ -66,21 +72,6 @@ class SendTransactionActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
return super.onKeyDown(keyCode, event)
}
public override fun onResume() {
super.onResume()
nfcManager.onResume()
}
public override fun onPause() {
super.onPause()
nfcManager.onPause()
}
public override fun onStop() {
super.onStop()
nfcManager.onStop()
}
override fun onTagDiscovered(tag: Tag) {
try {
nfcManager.ignoreTag(tag)

View file

@ -0,0 +1,30 @@
package com.tangem.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import com.tangem.wallet.R
class SettingsActivity : AppCompatActivity() {
companion object {
fun callingIntent(context: Context) = Intent(context, SettingsActivity::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
initToolbar()
}
private fun initToolbar() {
val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.setTitle(R.string.settings)
toolbar.setNavigationIcon(android.R.drawable.ic_menu_close_clear_cancel)
setSupportActionBar(toolbar)
toolbar.setNavigationOnClickListener { finish() }
}
}

View file

@ -1,14 +1,14 @@
package com.tangem.presentation.activity
package com.tangem.ui.activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import com.tangem.App
import com.tangem.Constant
import com.tangem.di.Navigator
import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.fragment.VerifyCard
import com.tangem.wallet.TangemContext
import com.tangem.ui.fragment.VerifyCard
import com.tangem.wallet.R
import javax.inject.Inject
@ -29,7 +29,7 @@ class VerifyCardActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_verify_card)
App.getNavigatorComponent().inject(this)
App.navigatorComponent.inject(this)
}
override fun onBackPressed() {

View file

@ -1,9 +1,9 @@
package com.tangem.presentation.dialog
package com.tangem.ui.dialog
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import android.support.v4.app.DialogFragment
import androidx.fragment.app.DialogFragment
import com.tangem.wallet.R

View file

@ -1,18 +1,20 @@
package com.tangem.presentation.dialog;
package com.tangem.ui.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import com.tangem.Constant;
import com.tangem.wallet.R;
public class PINSwapWarningDialog extends DialogFragment {
public static final String TAG = PINSwapWarningDialog.class.getSimpleName();
public static final String EXTRA_MESSAGE = "message";
private String message;
private String message = "";
private OnPositiveButton mOnPositiveButton;
public interface OnPositiveButton {
@ -26,12 +28,14 @@ public class PINSwapWarningDialog extends DialogFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
message = getArguments().getString(EXTRA_MESSAGE);
if (getArguments() != null) {
message = getArguments().getString(Constant.EXTRA_MESSAGE);
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setIcon(R.drawable.tangem_logo_small_new)
.setTitle(R.string.your_money_is_at_risk)
@ -43,7 +47,7 @@ public class PINSwapWarningDialog extends DialogFragment {
}
@Override
public void onCancel(DialogInterface dialog) {
public void onCancel(@NonNull DialogInterface dialog) {
super.onCancel(dialog);
}

View file

@ -1,9 +1,9 @@
package com.tangem.presentation.dialog
package com.tangem.ui.dialog
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import android.support.v4.app.DialogFragment
import androidx.fragment.app.DialogFragment
import com.tangem.wallet.R

View file

@ -1,9 +1,7 @@
package com.tangem.presentation.dialog;
package com.tangem.ui.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.os.Bundle;
@ -16,10 +14,15 @@ import android.widget.TextView;
import com.tangem.util.UtilHelper;
import com.tangem.wallet.R;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.DialogFragment;
/**
* Created by dvol on 06.03.2018.
*/
public class ShowQRCodeDialog extends DialogFragment {
public static final String TAG = ShowQRCodeDialog.class.getSimpleName();
private ImageView ivQR;
private TextView tvQRaddress;
private Bitmap bmQR;
@ -66,11 +69,11 @@ public class ShowQRCodeDialog extends DialogFragment {
}
}
public static void show(final Activity activity, final String content) {
public static void show(final AppCompatActivity activity, final String content) {
activity.runOnUiThread(() -> {
ShowQRCodeDialog instance = new ShowQRCodeDialog();
instance.setup(content);
instance.show(activity.getFragmentManager(), "ShowQRCodeDialog");
instance.show(activity.getSupportFragmentManager(), TAG);
});
}

View file

@ -0,0 +1,175 @@
package com.tangem.ui.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.ProgressBar;
import com.tangem.card_common.util.Log;
import com.tangem.wallet.R;
import java.util.Timer;
import java.util.TimerTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.DialogFragment;
/**
* Created by dvol on 06.03.2018.
*/
public class WaitSecurityDelayDialog extends DialogFragment {
public static final String TAG = WaitSecurityDelayDialog.class.getSimpleName();
private ProgressBar progressBar;
private int msTimeout = 60000, msProgress = 0;
private Timer timer;
private static Timer timerToShowDelayDialog = null;
private static WaitSecurityDelayDialog instance = null;
private final static int minRemainingDelayToShowDialog = 1000;
private final static int delayBeforeShowDialog = 5000;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = getActivity().getLayoutInflater();
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(() -> {
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(R.string.security_delay)
.setView(v)
.setCancelable(false)
.create();
}
@Override
public void onCancel(@NonNull DialogInterface dialog) {
super.onCancel(dialog);
}
public static void onReadBeforeRequest(final AppCompatActivity activity, final int timeout) {
activity.runOnUiThread(() -> {
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.getSupportFragmentManager(), TAG);
}
}, delayBeforeShowDialog);
});
}
public static void onReadAfterRequest(final Activity activity) {
activity.runOnUiThread(() -> {
if (timerToShowDelayDialog == null) return;
timerToShowDelayDialog.cancel();
timerToShowDelayDialog = null;
});
}
public static void onReadWait(final AppCompatActivity activity, final int msec) {
Log.e(TAG, "onReadWait callback(" + msec + ")");
activity.runOnUiThread(() -> {
Log.e(TAG, "onReadWait on ui thread(" + msec + ")");
if (timerToShowDelayDialog != null) {
timerToShowDelayDialog.cancel();
timerToShowDelayDialog = null;
}
if (msec == 0) {
if (instance != null) {
if (instance.isAdded()) {
instance.dismiss();
instance = null;
} else {
Log.e(TAG, "onReadWait(0) with not added dialog");
// instance.progressBar.postDelayed(() -> {
// Log.e(TAG, "onReadWait(0) dismiss delayed");
try {
instance.dismiss();
instance = null;
}
catch (Exception e)
{
e.printStackTrace();
}
// }, 10000);
}
}
return;
}
if (instance == null) {
if (msec > delayBeforeShowDialog + minRemainingDelayToShowDialog) {
instance = new WaitSecurityDelayDialog();
// 1000ms - card delay notification interval
instance.setup(msec + 1000, 1000);
instance.setCancelable(false);
instance.show(activity.getSupportFragmentManager(), TAG);
}
} else
instance.setRemainingTimeout(msec);
});
}
private void setup(int msTimeout, int msProgress) {
this.msTimeout = msTimeout;
this.msProgress = msProgress;
}
private void setRemainingTimeout(final int msec) {
progressBar.post(() -> {
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);
}
});
}
}

View file

@ -0,0 +1,156 @@
package com.tangem.ui.dialog
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AppCompatDialogFragment
import android.widget.ProgressBar
import com.tangem.ui.event.ReadAfterRequest
import com.tangem.ui.event.ReadBeforeRequest
import com.tangem.ui.event.ReadWait
import com.tangem.util.LOG
import com.tangem.wallet.R
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import java.util.*
class WaitSecurityDelayDialogNew : AppCompatDialogFragment() {
companion object {
val TAG: String = WaitSecurityDelayDialogNew::class.java.simpleName
private const val MIN_REMAINING_DELAY_TO_SHOW_DIALOG = 1000
private const val DELAY_BEFORE_SHOW_DIALOG = 5000
}
private lateinit var pb: ProgressBar
private var msTimeout = 60000
private var msProgress = 0
private var timer: Timer? = null
private var timerToShowDelayDialog: Timer? = null
@SuppressLint("InflateParams")
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val inflater = activity!!.layoutInflater
val v = inflater.inflate(R.layout.dialog_wait_pin2, null)
pb = v.findViewById(R.id.progressBar)
pb.max = msTimeout
pb.progress = msProgress
timer = Timer()
timer!!.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
pb.post {
val progress = pb.progress
if (progress < pb.max)
pb.progress = progress + 1000
}
}
}, 1000, 1000)
return AlertDialog.Builder(activity)
.setIcon(R.drawable.tangem_logo_small_new)
.setTitle(R.string.security_delay)
.setView(v)
.setCancelable(false)
.create()
}
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
@Subscribe
fun readBeforeRequest(readBeforeRequest: ReadBeforeRequest) {
LOG.i(TAG, "readBeforeRequest 111")
if (timerToShowDelayDialog != null || readBeforeRequest.timeout!! < DELAY_BEFORE_SHOW_DIALOG + MIN_REMAINING_DELAY_TO_SHOW_DIALOG)
return
timerToShowDelayDialog = Timer()
timerToShowDelayDialog!!.schedule(object : TimerTask() {
override fun run() {
setup(readBeforeRequest.timeout!!, DELAY_BEFORE_SHOW_DIALOG)
isCancelable = false
// if (!isAdded)
show(activity!!.supportFragmentManager, TAG)
// if (isHidden)
// activity?.supportFragmentManager?.let { show(it, TAG) }
}
}, DELAY_BEFORE_SHOW_DIALOG.toLong())
}
@Subscribe
fun readAfterRequest(readAfterRequest: ReadAfterRequest) {
LOG.i(TAG, "readAfterRequest 222")
if (timerToShowDelayDialog == null)
return
timerToShowDelayDialog!!.cancel()
timerToShowDelayDialog = null
}
@Subscribe
fun readWait(readWait: ReadWait) {
LOG.i(TAG, "readWait 333")
if (timerToShowDelayDialog != null) {
timerToShowDelayDialog!!.cancel()
timerToShowDelayDialog = null
}
if (readWait.msec == 0) {
dismiss()
return
}
if (readWait.msec!! > MIN_REMAINING_DELAY_TO_SHOW_DIALOG) {
// 1000ms - card delay notification interval
setup(readWait.msec!! + 1000, 1000)
isCancelable = false
if (isHidden)
activity?.supportFragmentManager?.let { show(it, TAG) }
} else
setRemainingTimeout(readWait.msec!!)
}
private fun setup(msTimeout: Int, msProgress: Int) {
this.msTimeout = msTimeout
this.msProgress = msProgress
}
private fun setRemainingTimeout(msec: Int) {
pb.post {
val progress = pb.progress
if (timer != null) {
// we get delay latency from card for first time - don't change progress by timer, only by card answer
pb.max = progress + msec
timer!!.cancel()
timer = null
} else {
val newProgress = pb.max - msec
if (pb.max > progress)
pb.progress = newProgress
else
pb.max = progress + msec
}
}
}
}

View file

@ -0,0 +1,3 @@
package com.tangem.ui.event
class DeletingWalletFinish

View file

@ -0,0 +1,3 @@
package com.tangem.ui.event
class ReadAfterRequest

View file

@ -0,0 +1,5 @@
package com.tangem.ui.event
class ReadBeforeRequest {
var timeout: Int? = null
}

View file

@ -0,0 +1,5 @@
package com.tangem.ui.event
class ReadWait {
var msec: Int? = null
}

Some files were not shown because too many files have changed in this diff Show more