diff --git a/.gitignore b/.gitignore
index 61cfae2fdc..16e0697372 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,6 +14,7 @@
.idea/runConfigurations.xml
.idea/encodings.xml
.idea/codeStyles/codeStyleConfig.xml
+.idea/assetWizardSettings.xml
# fastlane files
**/fastlane/report.xml
diff --git a/app/build.gradle b/app/build.gradle
index 9873896161..983516888d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,6 +2,7 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
+apply plugin: 'io.fabric'
static def generateVersionName() {
def gitCommitsCount = "git rev-list HEAD --count".execute().text.trim()
@@ -9,6 +10,9 @@ static def generateVersionName() {
return gitCommitsCount.toInteger() + 831// + "\\n" + gitCurrentBranch
}
+Properties properties = new Properties()
+properties.load(project.rootProject.file('local.properties').newDataInputStream())
+
android {
compileSdkVersion 28
defaultConfig {
@@ -25,19 +29,30 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug
+ buildConfigField 'boolean', 'CRASHLYTICS', 'Boolean.parseBoolean("false")'
+ ext.enableCrashlytics = false
}
debug {
debuggable true
minifyEnabled false
applicationIdSuffix ".debug"
signingConfig signingConfigs.debug
+ buildConfigField 'boolean', 'CRASHLYTICS', 'Boolean.parseBoolean("false")'
+ ext.enableCrashlytics = false
+ }
+ debug_beta {
+ initWith debug
+ versionNameSuffix "-beta"
+ applicationIdSuffix ".debug_beta"
+ buildConfigField 'boolean', 'CRASHLYTICS', 'Boolean.parseBoolean("true")'
+ manifestPlaceholders = [fabric_api_key: properties.getProperty('fabric_api_key')]
}
}
flavorDimensions ""
productFlavors {
tangemAccess {
- versionCode 114
- versionName "0.908.1." + generateVersionName()
+ versionCode project.hasProperty('versionCode') ? project.property('versionCode') as int : 116
+ versionName project.hasProperty('versionName') ? project.property('versionName') : "0.1.0.-SNAPSHOT"
applicationId "com.tangem.wallet"
signingConfig signingConfigs.debug
}
@@ -65,6 +80,7 @@ android {
repositories {
maven { url "https://jitpack.io" }
+ maven { url 'https://maven.fabric.io/public' }
}
configurations {
all*.exclude group: 'com.google.guava', module: 'listenablefuture'
@@ -119,6 +135,9 @@ dependencies {
// implementation 'com.github.stellar:java-stellar-sdk:0.4.1'
implementation 'net.i2p.crypto:eddsa:0.3.0'
+ implementation('com.crashlytics.sdk.android:crashlytics:2.9.9') { transitive = true }
+ implementation('io.fabric.sdk.android:fabric:1.4.0@aar') { transitive = true }
+
implementation files('libs/ripple-core-0.0.1.jar')
//4 dependencies for ripple-core TODO: move to module?
implementation 'net.i2p.crypto:eddsa:0.3.0'
diff --git a/app/src/debug/ic_launcher-web.png b/app/src/debug/ic_launcher-web.png
new file mode 100644
index 0000000000..6ea2968959
Binary files /dev/null and b/app/src/debug/ic_launcher-web.png differ
diff --git a/app/src/debug/res/drawable/ic_launcher_background.xml b/app/src/debug/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..2408e30d17
--- /dev/null
+++ b/app/src/debug/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..c4a603d4cc
--- /dev/null
+++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..c4a603d4cc
--- /dev/null
+++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher.png b/app/src/debug/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..32c50f2332
Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..d5b16e9389
Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..27632f83cf
Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher.png b/app/src/debug/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..5b7966cdd4
Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..626b524a19
Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..2d7e470a28
Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..54a64c94f6
Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..a88c8e24f4
Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..c87f5f573d
Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..9847879c7c
Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..40b595210b
Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..be2d53828c
Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..dab3199415
Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..92e1668b94
Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..d4c5bcabcb
Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml
index 9ef8838437..1c13a1dbc0 100644
--- a/app/src/debug/res/values/strings.xml
+++ b/app/src/debug/res/values/strings.xml
@@ -2,5 +2,6 @@
DEBUG
+ com.tangem.debug.data.LogFileProvider
\ No newline at end of file
diff --git a/app/src/debug_beta/AndroidManifest.xml b/app/src/debug_beta/AndroidManifest.xml
new file mode 100644
index 0000000000..9f07ca1097
--- /dev/null
+++ b/app/src/debug_beta/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/debug_beta/res/drawable/ic_launcher_background.xml b/app/src/debug_beta/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..2408e30d17
--- /dev/null
+++ b/app/src/debug_beta/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/debug_beta/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug_beta/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..c4a603d4cc
--- /dev/null
+++ b/app/src/debug_beta/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/debug_beta/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug_beta/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..c4a603d4cc
--- /dev/null
+++ b/app/src/debug_beta/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/debug_beta/res/mipmap-hdpi/ic_launcher.png b/app/src/debug_beta/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..2789587d99
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/debug_beta/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/debug_beta/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..33ac538a5c
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug_beta/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug_beta/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..db6d60f2c8
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/debug_beta/res/mipmap-mdpi/ic_launcher.png b/app/src/debug_beta/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..807a7a3be8
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/debug_beta/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/debug_beta/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..c91c20c714
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug_beta/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug_beta/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..8c87ad2ac3
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher.png b/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..d525ea41b9
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..578f07c490
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..fb770fede5
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher.png b/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..5e640bd233
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..0489350858
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..31e3b01ea5
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..f1730013fc
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000000..e1c04f31c9
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..1847464cbd
Binary files /dev/null and b/app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/debug_beta/res/values/strings.xml b/app/src/debug_beta/res/values/strings.xml
new file mode 100644
index 0000000000..3e2d147b31
--- /dev/null
+++ b/app/src/debug_beta/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ BetaTangem
+
+
\ No newline at end of file
diff --git a/app/src/main/ic_launcher_2-web.png b/app/src/main/ic_launcher_2-web.png
new file mode 100644
index 0000000000..8639ebe991
Binary files /dev/null and b/app/src/main/ic_launcher_2-web.png differ
diff --git a/app/src/main/java/com/tangem/App.kt b/app/src/main/java/com/tangem/App.kt
index 64d16a74e3..84e791d4ae 100644
--- a/app/src/main/java/com/tangem/App.kt
+++ b/app/src/main/java/com/tangem/App.kt
@@ -2,16 +2,18 @@ package com.tangem
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
+import com.crashlytics.android.Crashlytics
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.data.local.PendingTransactionsStorage
import com.tangem.di.*
import com.tangem.server_android.data.LocalStorage
import com.tangem.wallet.BuildConfig
+import io.fabric.sdk.android.Fabric
import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
@@ -73,6 +75,7 @@ class App : Application() {
}
}
)
+ if (BuildConfig.CRASHLYTICS) Fabric.with(this, Crashlytics())
}
}
diff --git a/app/src/main/java/com/tangem/data/network/BlockchainInfoApi.java b/app/src/main/java/com/tangem/data/network/BlockchainInfoApi.java
new file mode 100644
index 0000000000..70f4aae91d
--- /dev/null
+++ b/app/src/main/java/com/tangem/data/network/BlockchainInfoApi.java
@@ -0,0 +1,25 @@
+package com.tangem.data.network;
+
+import com.tangem.data.network.model.BlockchainInfoAddress;
+import com.tangem.data.network.model.BlockchainInfoUnspents;
+
+import io.reactivex.Single;
+import okhttp3.ResponseBody;
+import retrofit2.http.Field;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
+import retrofit2.http.POST;
+import retrofit2.http.Path;
+import retrofit2.http.Query;
+
+public interface BlockchainInfoApi {
+ @GET(Server.ApiBlockchainInfo.Method.ADDRESS)
+ Single blockchainInfoAddress(@Path("address") String address);
+
+ @GET(Server.ApiBlockchainInfo.Method.UTXO)
+ Single blockchainInfoUnspents(@Query("active") String address);
+
+ @FormUrlEncoded
+ @POST(Server.ApiBlockchainInfo.Method.PUSH)
+ Single blockchainInfoPush(@Field("tx") String tx);
+}
diff --git a/app/src/main/java/com/tangem/data/network/CoinmarketApi.java b/app/src/main/java/com/tangem/data/network/CoinmarketApi.java
index 0411ca2100..fd3699ba08 100644
--- a/app/src/main/java/com/tangem/data/network/CoinmarketApi.java
+++ b/app/src/main/java/com/tangem/data/network/CoinmarketApi.java
@@ -3,7 +3,6 @@ package com.tangem.data.network;
import com.tangem.data.network.model.RateInfoResponse;
import io.reactivex.Observable;
-import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Headers;
import retrofit2.http.Query;
diff --git a/app/src/main/java/com/tangem/data/network/Server.java b/app/src/main/java/com/tangem/data/network/Server.java
index 943488dc02..1b0570150a 100644
--- a/app/src/main/java/com/tangem/data/network/Server.java
+++ b/app/src/main/java/com/tangem/data/network/Server.java
@@ -118,4 +118,15 @@ public class Server {
static final String PUSH = MAIN + "/txs/push";
}
}
+
+ public static class ApiBlockchainInfo {
+ public static final String URL_BLOCKCHAININFO = ServerURL.API_BLOCKCHAIN_INFO;
+
+ public static class Method {
+ static final String ADDRESS = URL_BLOCKCHAININFO + "rawaddr/{address}?limit=5";
+ static final String UTXO = URL_BLOCKCHAININFO + "unspent";
+// static final String TX = URL_BLOCKCHAININFO + "rawtx/{txHash}";
+ static final String PUSH = URL_BLOCKCHAININFO + "pushtx";
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/data/network/ServerApiBlockchainInfo.java b/app/src/main/java/com/tangem/data/network/ServerApiBlockchainInfo.java
new file mode 100644
index 0000000000..3697332b26
--- /dev/null
+++ b/app/src/main/java/com/tangem/data/network/ServerApiBlockchainInfo.java
@@ -0,0 +1,46 @@
+package com.tangem.data.network;
+
+import android.util.Log;
+
+import com.tangem.App;
+import com.tangem.data.network.model.BlockchainInfoAddress;
+import com.tangem.data.network.model.BlockchainInfoAddressAndUnspents;
+import com.tangem.data.network.model.BlockchainInfoUnspents;
+
+import java.util.ArrayList;
+
+import io.reactivex.Single;
+import io.reactivex.SingleObserver;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.ResponseBody;
+
+public class ServerApiBlockchainInfo {
+ private static String TAG = ServerApiBlockchainInfo.class.getSimpleName();
+
+ public void getAddressAndUnspents(String wallet, SingleObserver addressAndUnspentsObserver) {
+ Log.i(TAG, "new getAddressAndUnspents request");
+ BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
+
+ Single addressObservable = api.blockchainInfoAddress(wallet);
+
+ Single unspentsObservable = api.blockchainInfoUnspents(wallet)
+ .onErrorReturnItem(new BlockchainInfoUnspents(new ArrayList<>()));
+
+ Single.zip(addressObservable, unspentsObservable, BlockchainInfoAddressAndUnspents::new)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(addressAndUnspentsObserver);
+ }
+
+ public void sendTransaction(String tx, SingleObserver sendObserver) {
+ Log.i(TAG, "new getAddress request");
+ BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
+
+ Single sendObservable = api.blockchainInfoPush(tx)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+
+ sendObservable.subscribe(sendObserver);
+ }
+}
diff --git a/app/src/main/java/com/tangem/data/network/ServerApiBlockcypher.java b/app/src/main/java/com/tangem/data/network/ServerApiBlockcypher.java
index f593b1545c..6b8bc2a6f1 100644
--- a/app/src/main/java/com/tangem/data/network/ServerApiBlockcypher.java
+++ b/app/src/main/java/com/tangem/data/network/ServerApiBlockcypher.java
@@ -2,6 +2,8 @@ package com.tangem.data.network;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.tangem.App;
import com.tangem.data.Blockchain;
import com.tangem.data.network.model.BlockcypherBody;
@@ -11,13 +13,12 @@ import com.tangem.data.network.model.BlockcypherTx;
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();
+ private static String TAG = ServerApiBlockcypher.class.getSimpleName();
public static final String BLOCKCYPHER_ADDRESS = "blockcypher_address";
public static final String BLOCKCYPHER_FEE = "blockcypher_fee";
diff --git a/app/src/main/java/com/tangem/data/network/ServerApiEos.java b/app/src/main/java/com/tangem/data/network/ServerApiEos.java
index 81f666b50b..99bc68b9cb 100644
--- a/app/src/main/java/com/tangem/data/network/ServerApiEos.java
+++ b/app/src/main/java/com/tangem/data/network/ServerApiEos.java
@@ -7,18 +7,16 @@ import com.tangem.wallet.eos.EosPushTransactionRequest;
import io.jafka.jeos.EosApi;
import io.jafka.jeos.EosApiFactory;
-import io.jafka.jeos.core.request.chain.transaction.PushTransactionRequest;
import io.jafka.jeos.core.response.chain.account.Account;
import io.jafka.jeos.core.response.chain.transaction.PushedTransaction;
import io.jafka.jeos.impl.EosApiServiceGenerator;
-import io.jafka.jeos.impl.EosChainApiService;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class ServerApiEos {
- private static String TAG = ServerApiBinance.class.getSimpleName();
+ private static String TAG = ServerApiEos.class.getSimpleName();
public static void getBalance(String wallet, Observer accountObserver) {
Log.i(TAG, "new getBalance request");
diff --git a/app/src/main/java/com/tangem/data/network/ServerApiRipple.java b/app/src/main/java/com/tangem/data/network/ServerApiRipple.java
index ddbe82e090..e4c82722fb 100644
--- a/app/src/main/java/com/tangem/data/network/ServerApiRipple.java
+++ b/app/src/main/java/com/tangem/data/network/ServerApiRipple.java
@@ -2,12 +2,13 @@ package com.tangem.data.network;
import android.util.Log;
+import androidx.annotation.NonNull;
+
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;
@@ -25,7 +26,14 @@ public class ServerApiRipple {
private int requestsCount = 0;
- public static String lastNode;
+ private final String rippleURL1 = "https://s1.ripple.com:51234"; //TODO: make random selection, add more?, move
+ private final String rippleURL2 = "https://s2.ripple.com:51234";
+
+ private String currentURL = rippleURL1;
+
+ public String getCurrentURL() {
+ return currentURL;
+ }
public boolean isRequestsSequenceCompleted() {
Log.i(TAG, String.format("isRequestsSequenceCompleted: %s (%d requests left)", String.valueOf(requestsCount <= 0), requestsCount));
@@ -46,11 +54,9 @@ public class ServerApiRipple {
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)
+ .baseUrl(currentURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
@@ -93,6 +99,38 @@ public class ServerApiRipple {
rippleBody = new RippleBody();
}
+ Call call = rippleApi.ripple(rippleBody);
+ call.enqueue(new Callback() {
+ @Override
+ public void onResponse(@NonNull Call call, @NonNull Response response) {
+ if (response.code() == 200) {
+ requestsCount--;
+ responseListener.onSuccess(method, response.body());
+ Log.i(TAG, "requestData " + method + " onResponse " + response.code());
+ } else {
+ retryRequest(method, rippleBody);
+ Log.e(TAG, "requestData " + method + " onResponse " + response.code());
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Call call, @NonNull Throwable t) {
+ retryRequest(method, rippleBody);
+ Log.e(TAG, "requestData " + method + " onFailure " + t.getMessage());
+ }
+ });
+ }
+
+ private void retryRequest(String method, RippleBody rippleBody) {
+ currentURL = rippleURL2;
+
+ Retrofit retrofitRipple = new Retrofit.Builder()
+ .baseUrl(currentURL)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build();
+
+ RippleApi rippleApi = retrofitRipple.create(RippleApi.class);
+
Call call = rippleApi.ripple(rippleBody);
call.enqueue(new Callback() {
@Override
diff --git a/app/src/main/java/com/tangem/data/network/ServerURL.java b/app/src/main/java/com/tangem/data/network/ServerURL.java
index 362e064d50..45515132d2 100644
--- a/app/src/main/java/com/tangem/data/network/ServerURL.java
+++ b/app/src/main/java/com/tangem/data/network/ServerURL.java
@@ -14,4 +14,5 @@ class ServerURL {
static final String API_MATIC_TESTNET = "https://testnet2.matic.network";
static final String API_STELLAR = "https://horizon.stellar.org/";
static final String API_STELLAR_TESTNET = "https://horizon-testnet.stellar.org";
+ static final String API_BLOCKCHAIN_INFO = "https://blockchain.info/";
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/data/network/model/BlockchainInfoResponse.kt b/app/src/main/java/com/tangem/data/network/model/BlockchainInfoResponse.kt
new file mode 100644
index 0000000000..fab1d0c88f
--- /dev/null
+++ b/app/src/main/java/com/tangem/data/network/model/BlockchainInfoResponse.kt
@@ -0,0 +1,43 @@
+package com.tangem.data.network.model
+
+import com.google.gson.annotations.SerializedName
+
+data class BlockchainInfoAddress(
+ @SerializedName("final_balance")
+ var final_balance: Long? = null,
+
+ @SerializedName("txs")
+ var txs: List? = null
+)
+
+data class BlockchainInfoTransaction(
+ @SerializedName("hash")
+ var hash: String? = null,
+
+ @SerializedName("block_height")
+ var block_height: Long? = null
+)
+
+data class BlockchainInfoUnspents(
+ @SerializedName("unspent_outputs")
+ var unspent_outputs: List
+)
+
+data class BlockchainInfoUtxo(
+ @SerializedName("tx_hash_big_endian")
+ var tx_hash_big_endian: String? = null,
+
+ @SerializedName("tx_output_n")
+ var tx_output_n: Int? = null,
+
+ @SerializedName("value")
+ var value: Long? = null,
+
+ @SerializedName("script")
+ var script: String? = null
+)
+
+data class BlockchainInfoAddressAndUnspents(
+ var address: BlockchainInfoAddress,
+ var unspents: BlockchainInfoUnspents
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/di/NetworkComponent.kt b/app/src/main/java/com/tangem/di/NetworkComponent.kt
index 06182cb4a8..e877710081 100644
--- a/app/src/main/java/com/tangem/di/NetworkComponent.kt
+++ b/app/src/main/java/com/tangem/di/NetworkComponent.kt
@@ -1,14 +1,11 @@
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
+import java.net.Socket
+import javax.inject.Named
+import javax.inject.Singleton
@Singleton
@Component(modules = [NetworkModule::class])
@@ -38,6 +35,9 @@ interface NetworkComponent {
@get:Named(Server.ApiSoChain.URL)
val retrofitSoChain: Retrofit
+ @get:Named(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO)
+ val retrofitBlockchainInfo: Retrofit
+
@get:Named("socket")
val socket: Socket
diff --git a/app/src/main/java/com/tangem/di/NetworkModule.kt b/app/src/main/java/com/tangem/di/NetworkModule.kt
index 4f1da7af02..93e10159e6 100644
--- a/app/src/main/java/com/tangem/di/NetworkModule.kt
+++ b/app/src/main/java/com/tangem/di/NetworkModule.kt
@@ -3,21 +3,19 @@ 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
+import retrofit2.converter.scalars.ScalarsConverterFactory
+import java.io.IOException
+import java.net.InetSocketAddress
+import java.net.Socket
+import java.net.SocketException
+import javax.inject.Named
+import javax.inject.Singleton
@Module
internal class NetworkModule {
@@ -121,6 +119,20 @@ internal class NetworkModule {
return builder.build()
}
+ @Singleton
+ @Provides
+ @Named(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO)
+ fun provideRetrofitBlockchainInfo(): Retrofit {
+ val builder = Retrofit.Builder()
+ .baseUrl(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO)
+ .addConverterFactory(GsonConverterFactory.create())
+ .addConverterFactory(ScalarsConverterFactory.create())
+ .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+ if (BuildConfig.DEBUG)
+ builder.client(createOkHttpClient())
+ return builder.build()
+ }
+
private fun createOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(createHttpLoggingInterceptor()).build()
}
diff --git a/app/src/main/java/com/tangem/ui/activity/LogoActivity.kt b/app/src/main/java/com/tangem/ui/activity/LogoActivity.kt
index 72aa74090a..b12979bbf7 100644
--- a/app/src/main/java/com/tangem/ui/activity/LogoActivity.kt
+++ b/app/src/main/java/com/tangem/ui/activity/LogoActivity.kt
@@ -34,7 +34,7 @@ class LogoActivity : AppCompatActivity() {
App.navigatorComponent.inject(this)
- ivLogo.setOnClickListener { hide() }
+ clLogoContainer.setOnClickListener { hide() }
}
override fun onPostCreate(savedInstanceState: Bundle?) {
diff --git a/app/src/main/java/com/tangem/ui/activity/MainActivity.kt b/app/src/main/java/com/tangem/ui/activity/MainActivity.kt
index 075ff25409..df3c3218b7 100644
--- a/app/src/main/java/com/tangem/ui/activity/MainActivity.kt
+++ b/app/src/main/java/com/tangem/ui/activity/MainActivity.kt
@@ -41,14 +41,17 @@ class MainActivity : AppCompatActivity() {
// 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(NfcAdapter.EXTRA_TAG)
-// if (tag != null && onNfcReaderCallback != null)
-// onNfcReaderCallback?.onTagDiscovered(tag)
-// }
-// }
+ 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(NfcAdapter.EXTRA_TAG)
+ if (tag != null) {
+ val activeFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
+ ?.childFragmentManager?.primaryNavigationFragment
+ (activeFragment as? NfcAdapter.ReaderCallback)?.onTagDiscovered(tag)
+ }
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
diff --git a/app/src/main/java/com/tangem/wallet/btc/BtcData.java b/app/src/main/java/com/tangem/wallet/btc/BtcData.java
index 24d82d0ed0..ba4a6da025 100644
--- a/app/src/main/java/com/tangem/wallet/btc/BtcData.java
+++ b/app/src/main/java/com/tangem/wallet/btc/BtcData.java
@@ -19,17 +19,19 @@ public class BtcData extends CoinData {
private boolean useBlockcypher = false;
+ //for blockchain.info
+ private boolean hasUnconfirmed = false;
+
public String getUnspentInputsDescription() {
try {
int gatheredUnspents = 0;
- if( unspentTransactions==null ) return "";
+ if (unspentTransactions == null) return "";
for (int i = 0; i < unspentTransactions.size(); i++) {
- if (unspentTransactions.get(i).script != null && unspentTransactions.get(i).script.length() > 1) gatheredUnspents++;
+ if (unspentTransactions.get(i).script != null && unspentTransactions.get(i).script.length() > 1)
+ gatheredUnspents++;
}
return unspentTransactions.size() + " unspents (" + gatheredUnspents + " received)";
- }
- catch (Exception e)
- {
+ } catch (Exception e) {
e.printStackTrace();
return "";
}
@@ -86,6 +88,7 @@ public class BtcData extends CoinData {
}
}
if (B.containsKey("UseBlockcypher")) useBlockcypher = B.getBoolean("UseBlockcypher");
+ if (B.containsKey("HasUnconfirmed")) useBlockcypher = B.getBoolean("HasUnconfirmed");
}
@Override
@@ -102,6 +105,7 @@ public class BtcData extends CoinData {
if (balanceConfirmed != null) B.putLong("BalanceConfirmed", balanceConfirmed);
if (balanceUnconfirmed != null) B.putLong("BalanceUnconfirmed", balanceUnconfirmed);
if (useBlockcypher) B.putBoolean("UseBlockcypher", true);
+ if (hasUnconfirmed) B.putBoolean("HasUnconfirmed", true);
} catch (Exception e) {
Log.e("Can't save to bundle ", e.getMessage());
}
@@ -116,7 +120,11 @@ public class BtcData extends CoinData {
}
public CoinEngine.InternalAmount getBalanceInInternalUnits() {
- return new CoinEngine.InternalAmount(BigDecimal.valueOf(balanceConfirmed).add(BigDecimal.valueOf(balanceUnconfirmed)),"Satoshi");
+ if (balanceConfirmed != null && balanceUnconfirmed != null) {
+ return new CoinEngine.InternalAmount(BigDecimal.valueOf(balanceConfirmed).add(BigDecimal.valueOf(balanceUnconfirmed)), "Satoshi");
+ } else {
+ return null;
+ }
}
public Long getBalanceUnconfirmed() {
@@ -142,4 +150,12 @@ public class BtcData extends CoinData {
public void setUseBlockcypher(boolean useBlockcypher) {
this.useBlockcypher = useBlockcypher;
}
+
+ public boolean isHasUnconfirmed() {
+ return hasUnconfirmed;
+ }
+
+ public void setHasUnconfirmed(boolean hasUnconfirmed) {
+ this.hasUnconfirmed = hasUnconfirmed;
+ }
}
diff --git a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java
index 925401cabc..730ab8aa87 100644
--- a/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java
+++ b/app/src/main/java/com/tangem/wallet/btc/BtcEngine.java
@@ -12,14 +12,18 @@ import com.tangem.card_common.util.Util;
import com.tangem.data.Blockchain;
import com.tangem.data.local.PendingTransactionsStorage;
import com.tangem.data.network.Server;
+import com.tangem.data.network.ServerApiBlockchainInfo;
import com.tangem.data.network.ServerApiBlockcypher;
import com.tangem.data.network.ServerApiCommon;
-import com.tangem.data.network.ServerApiSoChain;
+import com.tangem.data.network.model.BlockchainInfoAddress;
+import com.tangem.data.network.model.BlockchainInfoAddressAndUnspents;
+import com.tangem.data.network.model.BlockchainInfoTransaction;
+import com.tangem.data.network.model.BlockchainInfoUnspents;
+import com.tangem.data.network.model.BlockchainInfoUtxo;
import com.tangem.data.network.model.BlockcypherFee;
import com.tangem.data.network.model.BlockcypherResponse;
import com.tangem.data.network.model.BlockcypherTx;
import com.tangem.data.network.model.BlockcypherTxref;
-import com.tangem.data.network.model.SoChain;
import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.util.DerEncodingUtil;
@@ -33,9 +37,8 @@ import com.tangem.wallet.TangemContext;
import com.tangem.wallet.Transaction;
import com.tangem.wallet.UnspentOutputInfo;
-import org.json.JSONException;
-
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
@@ -46,6 +49,10 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import io.reactivex.SingleObserver;
+import io.reactivex.observers.DisposableSingleObserver;
+import okhttp3.ResponseBody;
+
public class BtcEngine extends CoinEngine {
private static final String TAG = BtcEngine.class.getSimpleName();
@@ -263,7 +270,7 @@ public class BtcEngine extends CoinEngine {
// return;
// }
- if (coinData.getBalanceUnconfirmed() != 0) {
+ if (coinData.getBalanceUnconfirmed() != 0 || coinData.isHasUnconfirmed()) {
balanceValidator.setScore(0);
balanceValidator.setFirstLine("Transaction in progress");
balanceValidator.setSecondLine("Wait for confirmation in blockchain");
@@ -537,120 +544,67 @@ public class BtcEngine extends CoinEngine {
public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception {
ctx.setError(null);
- if (!coinData.isUseBlockcypher()) {
- final ServerApiSoChain serverApiSoChain = new ServerApiSoChain();
+ //SoChain request can be found at LtcEngine
+ if (!coinData.isUseBlockcypher() && ctx.getBlockchain() != Blockchain.BitcoinTestNet) {
+ final ServerApiBlockchainInfo serverApiBlockchainInfo = new ServerApiBlockchainInfo();
- ServerApiSoChain.AddressInfoListener addressInfoListener = new ServerApiSoChain.AddressInfoListener() {
+ SingleObserver addressAndUnspentsObserver = new DisposableSingleObserver() {
@Override
- public void onSuccess(SoChain.Response.AddressBalance response) {
- try {
- String walletAddress = response.getData().getAddress();
- if (!walletAddress.equals(coinData.getWallet())) {
- // todo - check
- throw new Exception("Invalid wallet address in answer!");
- }
- Long confBalance = convertToInternalAmount(convertToAmount(response.getData().getConfirmed_balance(), getBalanceCurrency())).longValueExact();
- Long unconfirmedBalance = convertToInternalAmount(convertToAmount(response.getData().getUnconfirmed_balance(), getBalanceCurrency())).longValueExact();
+ public void onSuccess(BlockchainInfoAddressAndUnspents blockchainInfoAddressAndUnspents) {
+ BlockchainInfoAddress blockchainInfoAddress = blockchainInfoAddressAndUnspents.getAddress();
+ BlockchainInfoUnspents blockchainInfoUnspents = blockchainInfoAddressAndUnspents.getUnspents();
+
+ if (blockchainInfoAddress.getFinal_balance() != null) {
coinData.setBalanceReceived(true);
- coinData.setBalanceConfirmed(confBalance);
- coinData.setBalanceUnconfirmed(unconfirmedBalance);
- coinData.setValidationNodeDescription(Server.ApiSoChain.URL);
- } catch (JSONException e) {
- e.printStackTrace();
- Log.e(TAG, "FAIL METHOD_GetBalance JSONException");
- ctx.setError("FAIL METHOD_GetBalance JSONException");
- } catch (Exception e) {
- e.printStackTrace();
- Log.e(TAG, "FAIL METHOD_GetBalance Exception");
- ctx.setError("FAIL METHOD_GetBalance Exception");
- }
- if (serverApiSoChain.isRequestsSequenceCompleted()) {
- if (!coinData.isUseBlockcypher()) {
- checkPending(blockchainRequestsCallbacks);
- } else {
- try {
- requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks);
- } catch (Exception e) {
- ctx.setError(e.getMessage());
- blockchainRequestsCallbacks.onComplete(false);
+ coinData.setBalanceConfirmed(blockchainInfoAddress.getFinal_balance());
+ coinData.setBalanceUnconfirmed(0L);
+ coinData.setValidationNodeDescription(Server.ApiBlockchainInfo.URL_BLOCKCHAININFO);
+
+ for (BlockchainInfoTransaction tx : blockchainInfoAddress.getTxs()) {
+ if (tx.getBlock_height() == null) {
+ coinData.setHasUnconfirmed(true);
+ }
+ }
+
+ if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) {
+ for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) {
+ String pendingTxId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx()))));
+ for (BlockchainInfoTransaction responseTx : blockchainInfoAddress.getTxs()) {
+ if (responseTx.getHash().equals(pendingTxId)) {
+ App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), pendingTx.getTx());
+ }
+ }
}
}
- } else {
- blockchainRequestsCallbacks.onProgress();
}
+ for (BlockchainInfoUtxo utxo : blockchainInfoUnspents.getUnspent_outputs()) {
+ BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction();
+ trUnspent.txID = utxo.getTx_hash_big_endian();
+ trUnspent.amount = utxo.getValue();
+ trUnspent.outputN = utxo.getTx_output_n();
+ trUnspent.script = utxo.getScript();
+ coinData.getUnspentTransactions().add(trUnspent);
+ }
+
+ blockchainRequestsCallbacks.onComplete(true);
}
@Override
- public void onSuccess(SoChain.Response.TxUnspent response) {
- String walletAddress = response.getData().getAddress();
- try {
- if (!walletAddress.equals(coinData.getWallet())) {
- // todo - check
- throw new Exception("Invalid wallet address in answer!");
- }
- coinData.getUnspentTransactions().clear();
- if (response.getData().getTxs() != null)
- for (SoChain.Response.TxUnspent.Data.Tx tx : response.getData().getTxs()) {
- BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction();
- trUnspent.txID = tx.getTxid();
- trUnspent.amount = convertToInternalAmount(convertToAmount(tx.getValue(), getBalanceCurrency())).longValueExact();
- trUnspent.outputN = tx.getOutput_no();
- trUnspent.script = tx.getScript_hex();
- coinData.getUnspentTransactions().add(trUnspent);
-
-// if (blockchainRequestsCallbacks.allowAdvance()) {
-// //serverApiSoChain.requestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash));
-// } else {
-// ctx.setError("Terminated by user");
-// }
- }
- } catch (Exception e) {
- e.printStackTrace();
- Log.e(TAG, "FAIL METHOD_ListUnspent JSONException");
- }
-
- if (serverApiSoChain.isRequestsSequenceCompleted()) {
- if (!coinData.isUseBlockcypher()) {
- checkPending(blockchainRequestsCallbacks);
- } else {
- try {
- requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks);
- } catch (Exception e) {
- ctx.setError(e.getMessage());
- blockchainRequestsCallbacks.onComplete(false);
- }
- }
- } else {
- blockchainRequestsCallbacks.onProgress();
- }
-
- }
-
- @Override
- public void onFail(String message) {
- Log.i(TAG, "onFail: " + message);
+ public void onError(Throwable e) {
+ Log.i(TAG, "onError: getAddress" + e.getMessage());
coinData.setUseBlockcypher(true);
-// ctx.setError(R.string.cannot_obtain_data_from_blockchain);
- if (serverApiSoChain.isRequestsSequenceCompleted()) {
-// blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError());
- try {
- requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks);
- } catch (Exception e) {
- ctx.setError(e.getMessage());
- blockchainRequestsCallbacks.onComplete(false);
- }
- } else {
- blockchainRequestsCallbacks.onProgress();
+ try {
+ requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks);
+ } catch (Exception ex) {
+ ctx.setError(ex.getMessage());
+ blockchainRequestsCallbacks.onComplete(false);
}
}
};
+ serverApiBlockchainInfo.getAddressAndUnspents(coinData.getWallet(), addressAndUnspentsObserver);
- serverApiSoChain.setAddressInfoListener(addressInfoListener);
-
- serverApiSoChain.requestAddressBalance(ctx.getBlockchain(), coinData.getWallet());
- serverApiSoChain.requestUnspentTx(ctx.getBlockchain(), coinData.getWallet());
} else {
final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
@@ -716,93 +670,48 @@ public class BtcEngine extends CoinEngine {
private void checkPending(BlockchainRequestsCallbacks blockchainRequestsCallbacks) {
if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) {
+ ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
- if (!coinData.isUseBlockcypher()) {
- ServerApiSoChain serverApiSoChain = new ServerApiSoChain();
-
- ServerApiSoChain.TransactionInfoListener listener = new ServerApiSoChain.TransactionInfoListener() {
- @Override
- public void onSuccess(SoChain.Response.GetTx response) {
- Log.i(TAG, "onSuccess: GetTx");
- try {
- if (response.getData() != null && response.getData().getTx_hex() != null && response.getData().getTxid() != null) {
- App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), response.getData().getTx_hex());
- }
- } catch (Exception e) {
- Log.e(TAG, "onFail: GetTx" + response);
- }
- if (serverApiSoChain.isRequestsSequenceCompleted()) {
- blockchainRequestsCallbacks.onComplete(!ctx.hasError());
- } else {
- blockchainRequestsCallbacks.onProgress();
- }
- }
-
- @Override
- public void onFail(String message) {
- Log.i(TAG, "onFail: GetTx " + message);
- if (serverApiSoChain.isRequestsSequenceCompleted()) {
- blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError());
- } else {
- blockchainRequestsCallbacks.onProgress();
- }
- }
- };
-
- serverApiSoChain.setTransactionInfoListener(listener);
- for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) {
- String txId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx()))));
+ ServerApiBlockcypher.TxResponseListener listener = new ServerApiBlockcypher.TxResponseListener() {
+ @Override
+ public void onSuccess(BlockcypherTx response) {
+ Log.i(TAG, "onSuccess: BlockcypherTx");
try {
- serverApiSoChain.requestTransactionInfo(ctx.getBlockchain(), txId);
+ if (response.getHex() != null) {
+ App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), response.getHex());
+ }
} catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "onFail: BlockcypherTx" + response);
+ }
+ if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ } else {
+ blockchainRequestsCallbacks.onProgress();
}
}
- } else {
- ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
- ServerApiBlockcypher.TxResponseListener listener = new ServerApiBlockcypher.TxResponseListener() {
-
- @Override
- public void onSuccess(BlockcypherTx response) {
- Log.i(TAG, "onSuccess: BlockcypherTx");
- try {
- if (response.getHex() != null) {
- App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), response.getHex());
- }
- } catch (Exception e) {
- Log.e(TAG, "onFail: BlockcypherTx" + response);
- }
- if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
- blockchainRequestsCallbacks.onComplete(!ctx.hasError());
- } else {
- blockchainRequestsCallbacks.onProgress();
- }
+ @Override
+ public void onFail(String message) {
+ Log.i(TAG, "onFail: BlockcypherTx " + message);
+ if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError());
+ } else {
+ blockchainRequestsCallbacks.onProgress();
}
+ }
+ };
+ serverApiBlockcypher.setTxResponseListener(listener);
- @Override
- public void onFail(String message) {
- Log.i(TAG, "onFail: BlockcypherTx " + message);
- if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
- blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError());
- } else {
- blockchainRequestsCallbacks.onProgress();
- }
- }
- };
- serverApiBlockcypher.setTxResponseListener(listener);
-
- for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) {
- String txId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx()))));
- try {
- serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_TXS, "", txId);
- } catch (Exception e) {
- e.printStackTrace();
- blockchainRequestsCallbacks.onComplete(!ctx.hasError());
- }
+ for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) {
+ String txId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx()))));
+ try {
+ serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_TXS, "", txId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
}
}
+
} else {
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
}
@@ -1026,42 +935,40 @@ public class BtcEngine extends CoinEngine {
@Override
public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception {
final String txStr = BTCUtils.toHex(txForSend);
- if (!coinData.isUseBlockcypher()) {
- final ServerApiSoChain serverApiSoChain = new ServerApiSoChain();
- ServerApiSoChain.SendTxListener listener = new ServerApiSoChain.SendTxListener() {
+ //SoChain request can be found at LtcEngine
+ if (!coinData.isUseBlockcypher() && ctx.getBlockchain() != Blockchain.BitcoinTestNet) {
+ final ServerApiBlockchainInfo serverApiBlockchainInfo = new ServerApiBlockchainInfo();
+
+ SingleObserver responseObserver = new DisposableSingleObserver() {
@Override
- public void onSuccess(SoChain.Response.SendTx response) {
+ public void onSuccess(ResponseBody response) {
try {
- if (response.getStatus() == null || response.getData() == null) {
- ctx.setError("Rejected by node: invalid answer received");
- blockchainRequestsCallbacks.onComplete(false);
- } else if (response.getStatus().equals("fail") || response.getData().getTxid() == null) {
- ctx.setError("Rejected by node: " + response.getData());
- 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);
+ Log.i(TAG, response.string());
+ if (!response.string().equals("Transaction Submitted")) {
+ ctx.setError(response.string());
}
+ } catch (IOException e) {
+ e.printStackTrace();
+ ctx.setError(e.getMessage());
+ }
+
+ if (ctx.hasError()) {
+ blockchainRequestsCallbacks.onComplete(false);
+ } else {
+ blockchainRequestsCallbacks.onComplete(true);
}
}
@Override
- public void onFail(String message) {
- ctx.setError(message);
+ public void onError(Throwable e) {
+ Log.e(TAG, e.getMessage());
+ ctx.setError(e.getMessage());
blockchainRequestsCallbacks.onComplete(false);
}
};
- serverApiSoChain.setSendTxListener(listener);
- serverApiSoChain.requestSendTransaction(ctx.getBlockchain(), txStr);
+
+ serverApiBlockchainInfo.sendTransaction(txStr, responseObserver);
} else {
final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
diff --git a/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java b/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java
index 2aca8f2f43..d0bae7e453 100644
--- a/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java
+++ b/app/src/main/java/com/tangem/wallet/ltc/LtcEngine.java
@@ -2,11 +2,22 @@ package com.tangem.wallet.ltc;
import android.net.Uri;
import android.text.InputFilter;
+import android.util.Log;
+import com.tangem.App;
import com.tangem.card_common.data.TangemCard;
import com.tangem.card_common.reader.CardProtocol;
import com.tangem.card_common.tasks.SignTask;
import com.tangem.card_common.util.Util;
+import com.tangem.data.local.PendingTransactionsStorage;
+import com.tangem.data.network.Server;
+import com.tangem.data.network.ServerApiBlockcypher;
+import com.tangem.data.network.ServerApiSoChain;
+import com.tangem.data.network.model.BlockcypherFee;
+import com.tangem.data.network.model.BlockcypherResponse;
+import com.tangem.data.network.model.BlockcypherTx;
+import com.tangem.data.network.model.BlockcypherTxref;
+import com.tangem.data.network.model.SoChain;
import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.util.DerEncodingUtil;
@@ -21,6 +32,8 @@ import com.tangem.wallet.UnspentOutputInfo;
import com.tangem.wallet.btc.BtcData;
import com.tangem.wallet.btc.BtcEngine;
+import org.json.JSONException;
+
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -478,6 +491,281 @@ public class LtcEngine extends BtcEngine {
};
}
+ @Override
+ public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) throws Exception {
+ ctx.setError(null);
+
+ if (!coinData.isUseBlockcypher()) {
+ final ServerApiSoChain serverApiSoChain = new ServerApiSoChain();
+
+ ServerApiSoChain.AddressInfoListener addressInfoListener = new ServerApiSoChain.AddressInfoListener() {
+ @Override
+ public void onSuccess(SoChain.Response.AddressBalance response) {
+ try {
+ String walletAddress = response.getData().getAddress();
+ if (!walletAddress.equals(coinData.getWallet())) {
+ // todo - check
+ throw new Exception("Invalid wallet address in answer!");
+ }
+ Long confBalance = convertToInternalAmount(convertToAmount(response.getData().getConfirmed_balance(), getBalanceCurrency())).longValueExact();
+ Long unconfirmedBalance = convertToInternalAmount(convertToAmount(response.getData().getUnconfirmed_balance(), getBalanceCurrency())).longValueExact();
+ coinData.setBalanceReceived(true);
+ coinData.setBalanceConfirmed(confBalance);
+ coinData.setBalanceUnconfirmed(unconfirmedBalance);
+ coinData.setValidationNodeDescription(Server.ApiSoChain.URL);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ Log.e(TAG, "FAIL METHOD_GetBalance JSONException");
+ ctx.setError("FAIL METHOD_GetBalance JSONException");
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "FAIL METHOD_GetBalance Exception");
+ ctx.setError("FAIL METHOD_GetBalance Exception");
+ }
+ if (serverApiSoChain.isRequestsSequenceCompleted()) {
+ if (!coinData.isUseBlockcypher()) {
+ checkPending(blockchainRequestsCallbacks);
+ } else {
+ try {
+ requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks);
+ } catch (Exception e) {
+ ctx.setError(e.getMessage());
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ }
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+
+ }
+
+ @Override
+ public void onSuccess(SoChain.Response.TxUnspent response) {
+ String walletAddress = response.getData().getAddress();
+ try {
+ if (!walletAddress.equals(coinData.getWallet())) {
+ // todo - check
+ throw new Exception("Invalid wallet address in answer!");
+ }
+ coinData.getUnspentTransactions().clear();
+ if (response.getData().getTxs() != null)
+ for (SoChain.Response.TxUnspent.Data.Tx tx : response.getData().getTxs()) {
+ BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction();
+ trUnspent.txID = tx.getTxid();
+ trUnspent.amount = convertToInternalAmount(convertToAmount(tx.getValue(), getBalanceCurrency())).longValueExact();
+ trUnspent.outputN = tx.getOutput_no();
+ trUnspent.script = tx.getScript_hex();
+ coinData.getUnspentTransactions().add(trUnspent);
+
+// if (blockchainRequestsCallbacks.allowAdvance()) {
+// //serverApiSoChain.requestData(ctx, ElectrumRequest.getTransaction(walletAddress, hash));
+// } else {
+// ctx.setError("Terminated by user");
+// }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "FAIL METHOD_ListUnspent JSONException");
+ }
+
+ if (serverApiSoChain.isRequestsSequenceCompleted()) {
+ if (!coinData.isUseBlockcypher()) {
+ checkPending(blockchainRequestsCallbacks);
+ } else {
+ try {
+ requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks);
+ } catch (Exception e) {
+ ctx.setError(e.getMessage());
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ }
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+
+ }
+
+ @Override
+ public void onFail(String message) {
+ Log.i(TAG, "onFail: " + message);
+ coinData.setUseBlockcypher(true);
+// ctx.setError(R.string.cannot_obtain_data_from_blockchain);
+ if (serverApiSoChain.isRequestsSequenceCompleted()) {
+// blockchainRequestsCallbacks.onComplete(false);//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError());
+ try {
+ requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks);
+ } catch (Exception e) {
+ ctx.setError(e.getMessage());
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+ }
+ };
+
+
+ serverApiSoChain.setAddressInfoListener(addressInfoListener);
+
+ serverApiSoChain.requestAddressBalance(ctx.getBlockchain(), coinData.getWallet());
+ serverApiSoChain.requestUnspentTx(ctx.getBlockchain(), coinData.getWallet());
+ } else {
+ final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
+
+ ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() {
+ @Override
+ public void onSuccess(String method, BlockcypherResponse blockcypherResponse) {
+ Log.i(TAG, "onSuccess: " + method);
+ try {
+ String walletAddress = blockcypherResponse.getAddress();
+ if (!walletAddress.equals(coinData.getWallet())) {
+ // todo - check
+ throw new Exception("Invalid wallet address in answer!");
+ }
+ Long confBalance = blockcypherResponse.getBalance();
+ Long unconfirmedBalance = blockcypherResponse.getUnconfirmed_balance();
+ coinData.setBalanceReceived(true);
+ coinData.setBalanceConfirmed(confBalance);
+ coinData.setBalanceUnconfirmed(unconfirmedBalance);
+ coinData.setValidationNodeDescription(Server.ApiBlockcypher.URL_BLOCKCYPHER);
+
+ coinData.getUnspentTransactions().clear();
+ if (blockcypherResponse.getTxrefs() != null) {
+ for (BlockcypherTxref txref : blockcypherResponse.getTxrefs()) {
+ BtcData.UnspentTransaction trUnspent = new BtcData.UnspentTransaction();
+ trUnspent.txID = txref.getTx_hash();
+ trUnspent.amount = txref.getValue();
+ trUnspent.outputN = txref.getTx_output_n();
+ trUnspent.script = txref.getScript();
+ coinData.getUnspentTransactions().add(trUnspent);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "FAIL BLOCKCYPHER_ADDRESS Exception");
+ }
+
+ if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
+ checkPending(blockchainRequestsCallbacks);
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+ }
+
+ public void onSuccess(String method, BlockcypherFee blockcypherFee) {
+ Log.e(TAG, "Wrong response type for requestBalanceAndUnspentTransactions");
+ ctx.setError("Wrong response type for requestBalanceAndUnspentTransactions");
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+
+ @Override
+ public void onFail(String method, String message) {
+ Log.i(TAG, "onFail: " + method + " " + message);
+ ctx.setError(message);
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ };
+
+ serverApiBlockcypher.setResponseListener(blockcypherListener);
+
+ serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_ADDRESS, ctx.getCoinData().getWallet(), "");
+ }
+ }
+
+ private void checkPending(BlockchainRequestsCallbacks blockchainRequestsCallbacks) {
+ if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) {
+
+ if (!coinData.isUseBlockcypher()) {
+ ServerApiSoChain serverApiSoChain = new ServerApiSoChain();
+
+ ServerApiSoChain.TransactionInfoListener listener = new ServerApiSoChain.TransactionInfoListener() {
+ @Override
+ public void onSuccess(SoChain.Response.GetTx response) {
+ Log.i(TAG, "onSuccess: GetTx");
+ try {
+ if (response.getData() != null && response.getData().getTx_hex() != null && response.getData().getTxid() != null) {
+ App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), response.getData().getTx_hex());
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "onFail: GetTx" + response);
+ }
+ if (serverApiSoChain.isRequestsSequenceCompleted()) {
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+ }
+
+ @Override
+ public void onFail(String message) {
+ Log.i(TAG, "onFail: GetTx " + message);
+ if (serverApiSoChain.isRequestsSequenceCompleted()) {
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError());
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+ }
+ };
+
+ serverApiSoChain.setTransactionInfoListener(listener);
+ for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) {
+ String txId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx()))));
+ try {
+ serverApiSoChain.requestTransactionInfo(ctx.getBlockchain(), txId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ }
+ }
+ } else {
+ ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
+
+ ServerApiBlockcypher.TxResponseListener listener = new ServerApiBlockcypher.TxResponseListener() {
+
+ @Override
+ public void onSuccess(BlockcypherTx response) {
+ Log.i(TAG, "onSuccess: BlockcypherTx");
+ try {
+ if (response.getHex() != null) {
+ App.pendingTransactionsStorage.removeTransaction(ctx.getCard(), response.getHex());
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "onFail: BlockcypherTx" + response);
+ }
+ if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+ }
+
+ @Override
+ public void onFail(String message) {
+ Log.i(TAG, "onFail: BlockcypherTx " + message);
+ if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());//serverApiElectrum.isErrorOccurred(), serverApiElectrum.getError());
+ } else {
+ blockchainRequestsCallbacks.onProgress();
+ }
+ }
+ };
+ serverApiBlockcypher.setTxResponseListener(listener);
+
+ for (PendingTransactionsStorage.TransactionInfo pendingTx : App.pendingTransactionsStorage.getTransactions(ctx.getCard()).getTransactions()) {
+ String txId = BTCUtils.toHex(BTCUtils.reverse(CryptoUtil.doubleSha256(BTCUtils.fromHex(pendingTx.getTx()))));
+ try {
+ serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_TXS, "", txId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ }
+ }
+ }
+ } else {
+ blockchainRequestsCallbacks.onComplete(!ctx.hasError());
+ }
+ }
+
private final static BigDecimal relayFee = new BigDecimal(0.00001).setScale(8, RoundingMode.DOWN);
@Override
@@ -486,6 +774,92 @@ public class LtcEngine extends BtcEngine {
blockchainRequestsCallbacks.onComplete(true);
}
+ @Override
+ public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) throws Exception {
+ final String txStr = BTCUtils.toHex(txForSend);
+ if (!coinData.isUseBlockcypher()) {
+ final ServerApiSoChain serverApiSoChain = new ServerApiSoChain();
+
+ ServerApiSoChain.SendTxListener listener = new ServerApiSoChain.SendTxListener() {
+ @Override
+ public void onSuccess(SoChain.Response.SendTx response) {
+ try {
+ if (response.getStatus() == null || response.getData() == null) {
+ ctx.setError("Rejected by node: invalid answer received");
+ blockchainRequestsCallbacks.onComplete(false);
+ } else if (response.getStatus().equals("fail") || response.getData().getTxid() == null) {
+ ctx.setError("Rejected by node: " + response.getData());
+ 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(String message) {
+ ctx.setError(message);
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ };
+ serverApiSoChain.setSendTxListener(listener);
+ serverApiSoChain.requestSendTransaction(ctx.getBlockchain(), txStr);
+
+ } else {
+ final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
+
+ ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() {
+ @Override
+ public void onSuccess(String method, BlockcypherResponse blockcypherResponse) {
+ String resultString = blockcypherResponse.toString();
+ try {
+ if (resultString.isEmpty()) {
+ ctx.setError("No response from node");
+ blockchainRequestsCallbacks.onComplete(false);
+ } else { // TODO: Make check for a valid send response
+ 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);
+ Log.e(TAG, resultString);
+ }
+ }
+ }
+
+ public void onSuccess(String method, BlockcypherFee blockcypherFee) {
+ Log.e(TAG, "Wrong response type for requestSendTransaction");
+ ctx.setError("Wrong response type for requestSendTransaction");
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+
+ @Override
+ public void onFail(String method, String message) {
+ Log.i(TAG, "onFail: " + method + " " + message);
+ ctx.setError(message);
+ blockchainRequestsCallbacks.onComplete(false);
+ }
+ };
+ serverApiBlockcypher.setResponseListener(blockcypherListener);
+
+ serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_SEND, "", txStr);
+ }
+ }
+
@Override
public boolean allowSelectFeeLevel() {
return false;
diff --git a/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java b/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java
index f7276a1f92..22baeab80d 100644
--- a/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java
+++ b/app/src/main/java/com/tangem/wallet/xrp/XrpEngine.java
@@ -455,7 +455,7 @@ public class XrpEngine extends CoinEngine {
coinData.setBalanceReceived(true);
coinData.setBalanceUnconfirmed(Long.parseLong(rippleResponse.getResult().getAccount_data().getBalance()));
coinData.setSequence(rippleResponse.getResult().getAccount_data().getSequence());
- coinData.setValidationNodeDescription(ServerApiRipple.lastNode);
+ coinData.setValidationNodeDescription(serverApiRipple.getCurrentURL());
// //check pending
// if (App.pendingTransactionsStorage.hasTransactions(ctx.getCard())) {
diff --git a/app/src/main/res/drawable/ic_launcher_background.png b/app/src/main/res/drawable/ic_launcher_background.png
new file mode 100644
index 0000000000..095189cdaf
Binary files /dev/null and b/app/src/main/res/drawable/ic_launcher_background.png differ
diff --git a/app/src/main/res/layout/activity_logo.xml b/app/src/main/res/layout/activity_logo.xml
index f0ddbec1a9..ee2df5a3e2 100644
--- a/app/src/main/res/layout/activity_logo.xml
+++ b/app/src/main/res/layout/activity_logo.xml
@@ -1,4 +1,5 @@
Erase wallet
- BETA v.%1$s \n dev \n build %2$s
- BETA v.%1$s
+ ALPHA v.%1$s \n dev \n build %2$s
+ v.%1$s
Cardano
diff --git a/build.gradle b/build.gradle
index 58b21916a3..0a6c7bd4a5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,11 +3,13 @@ buildscript {
repositories {
google()
jcenter()
+ maven { url 'https://maven.fabric.io/public' }
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.2'
+ classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
+ classpath 'io.fabric.tools:gradle:1.27.1'
}
}
diff --git a/card-android/build.gradle b/card-android/build.gradle
index 956d0c88fe..1dc0028332 100644
--- a/card-android/build.gradle
+++ b/card-android/build.gradle
@@ -20,6 +20,10 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
+ debug_beta {
+ initWith debug
+ versionNameSuffix "-beta"
+ }
}
buildToolsVersion '28.0.3'
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index bf598fdf5b..b70e5bad19 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -16,46 +16,59 @@
default_platform(:android)
platform :android do
+
desc "Runs all the tests"
lane :test do
gradle(
- task: "test",
- flavor: "tangemAccess",
- build_type: "Debug")
+ task: "test",
+ flavor: "tangemAccess",
+ build_type: "DebugUnitTest")
end
- desc "Build a release APK"
- lane :release do
- gradle(
- task: "clean assemble",
- flavor: "tangemAccess",
- build_type: "Release")
+ desc "Build a signed release APK"
+ lane :release do |options|
+ gradle(
+ task: "clean assemble",
+ flavor: "tangemAccess",
+ build_type: "Release",
+ properties: {
+ "android.injected.signing.store.file" => options[:keystore],
+ "android.injected.signing.store.password" => options[:store_password],
+ "android.injected.signing.key.alias" => options[:key_alias],
+ "android.injected.signing.key.password" => options[:key_password],
+ })
end
- desc "Build a debug APK"
- lane :debug do
- gradle(task: "clean assemble",
- flavor: "tangemAccess",
- build_type: "Debug")
- end
+ desc "Build debug and release APKs"
+ lane :build do |options|
+ gradle(
+ task: "clean assemble",
+ flavor: "tangemAccess",
+ build_type: "Debug_beta",
+ properties: {
+ 'versionCode' => options[:versionCode],
+ 'versionName' => options[:versionName],
+ })
+ gradle(
+ task: "assemble",
+ flavor: "tangemAccess",
+ build_type: "Release",
+ properties: {
+ 'versionCode' => options[:versionCode],
+ 'versionName' => options[:versionName],
+ "android.injected.signing.store.file" => options[:keystore],
+ "android.injected.signing.store.password" => options[:store_password],
+ "android.injected.signing.key.alias" => options[:key_alias],
+ "android.injected.signing.key.password" => options[:key_password],
+ })
+ end
desc "Submit a new Beta Build to Crashlytics Beta"
- lane :beta do
- gradle(task: "clean assemble",
- flavor: "tangemAccess",
- build_type: "Debug")
- crashlytics
-
- # sh "your_script.sh"
- # You can also use other beta testing services here
+ lane :beta do |options|
+ crashlytics(
+ api_token: options[:api_token],
+ build_secret: options[:build_secret],
+ apk_path: options[:apk_path])
end
- desc "Deploy a new version to the Google Play"
- lane :deploy do
- gradle(
- task: "clean assemble",
- flavor: "tangemAccess",
- build_type: "Release")
- upload_to_play_store
- end
end
diff --git a/fastlane/README.md b/fastlane/README.md
index 61b4a5d99a..7c3f972135 100644
--- a/fastlane/README.md
+++ b/fastlane/README.md
@@ -25,22 +25,17 @@ Runs all the tests
```
fastlane android release
```
-Build a release APK
-### android debug
+Build a signed release APK
+### android build
```
-fastlane android debug
+fastlane android build
```
-Build a debug APK
+Build debug and release APKs
### android beta
```
fastlane android beta
```
Submit a new Beta Build to Crashlytics Beta
-### android deploy
-```
-fastlane android deploy
-```
-Deploy a new version to the Google Play
----
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 662c6f9706..212388dbad 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Apr 18 12:21:12 MSK 2019
+#Wed Aug 21 11:57:23 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/server-android/build.gradle b/server-android/build.gradle
index 7aaa4af308..c6e7c5bfed 100644
--- a/server-android/build.gradle
+++ b/server-android/build.gradle
@@ -20,6 +20,10 @@ android {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
+ debug_beta {
+ initWith debug
+ versionNameSuffix "-beta"
+ }
}
productFlavors {
}