Updated on 2026-08-14
1
.gitignore
vendored
|
|
@ -14,6 +14,7 @@
|
|||
.idea/runConfigurations.xml
|
||||
.idea/encodings.xml
|
||||
.idea/codeStyles/codeStyleConfig.xml
|
||||
.idea/assetWizardSettings.xml
|
||||
|
||||
# fastlane files
|
||||
**/fastlane/report.xml
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
@ -123,6 +139,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'
|
||||
|
|
|
|||
BIN
app/src/debug/ic_launcher-web.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
74
app/src/debug/res/drawable/ic_launcher_background.xml
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
5
app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -2,5 +2,6 @@
|
|||
<resources>
|
||||
|
||||
<string name="app_name">DEBUG</string>
|
||||
<string name="log_file_provider_authorities">com.tangem.debug.data.LogFileProvider</string>
|
||||
|
||||
</resources>
|
||||
23
app/src/debug_beta/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.tangem.wallet">
|
||||
|
||||
|
||||
<application
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<meta-data
|
||||
android:name="io.fabric.ApiKey"
|
||||
android:value="${fabric_api_key}" />
|
||||
|
||||
<provider
|
||||
android:name="com.tangem.data.LogFileProvider"
|
||||
android:authorities="@string/log_file_provider_authorities"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
74
app/src/debug_beta/res/drawable/ic_launcher_background.xml
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#008577"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
5
app/src/debug_beta/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
BIN
app/src/debug_beta/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/debug_beta/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
app/src/debug_beta/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/debug_beta/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/debug_beta/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/debug_beta/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
app/src/debug_beta/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/debug_beta/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
app/src/debug_beta/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app/src/debug_beta/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
app/src/debug_beta/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
6
app/src/debug_beta/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">BetaTangem</string>
|
||||
|
||||
</resources>
|
||||
BIN
app/src/main/ic_launcher_2-web.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
|
@ -2,6 +2,7 @@ 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.card_android.android.data.Firmwares
|
||||
|
|
@ -15,6 +16,7 @@ import com.tangem.di.NetworkComponent
|
|||
import com.tangem.di.ToastHelperComponent
|
||||
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
|
||||
|
||||
|
|
@ -74,6 +76,7 @@ class App : Application() {
|
|||
}
|
||||
}
|
||||
)
|
||||
if (BuildConfig.CRASHLYTICS) Fabric.with(this, Crashlytics())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> blockchainInfoAddress(@Path("address") String address);
|
||||
|
||||
@GET(Server.ApiBlockchainInfo.Method.UTXO)
|
||||
Single<BlockchainInfoUnspents> blockchainInfoUnspents(@Query("active") String address);
|
||||
|
||||
@FormUrlEncoded
|
||||
@POST(Server.ApiBlockchainInfo.Method.PUSH)
|
||||
Single<ResponseBody> blockchainInfoPush(@Field("tx") String tx);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<BlockchainInfoAddressAndUnspents> addressAndUnspentsObserver) {
|
||||
Log.i(TAG, "new getAddressAndUnspents request");
|
||||
BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
|
||||
|
||||
Single<BlockchainInfoAddress> addressObservable = api.blockchainInfoAddress(wallet);
|
||||
|
||||
Single<BlockchainInfoUnspents> 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<ResponseBody> sendObserver) {
|
||||
Log.i(TAG, "new getAddress request");
|
||||
BlockchainInfoApi api = App.Companion.getNetworkComponent().getRetrofitBlockchainInfo().create(BlockchainInfoApi.class);
|
||||
|
||||
Single<ResponseBody> sendObservable = api.blockchainInfoPush(tx)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
|
||||
sendObservable.subscribe(sendObserver);
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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<Account> accountObserver) {
|
||||
Log.i(TAG, "new getBalance request");
|
||||
|
|
|
|||
|
|
@ -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<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 {
|
||||
retryRequest(method, rippleBody);
|
||||
Log.e(TAG, "requestData " + method + " onResponse " + response.code());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<RippleResponse> 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<RippleResponse> call = rippleApi.ripple(rippleBody);
|
||||
call.enqueue(new Callback<RippleResponse>() {
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -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/";
|
||||
}
|
||||
|
|
@ -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<BlockchainInfoTransaction>? = 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<BlockchainInfoUtxo>
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,14 +39,15 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
|
|||
|
||||
// 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 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) {
|
||||
onTagDiscovered(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class LogoFragment : BaseFragment() {
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
ivLogo.setOnClickListener { hide() }
|
||||
clLogoContainer.setOnClickListener { hide() }
|
||||
// set beta version name
|
||||
if (BuildConfig.DEBUG)
|
||||
tvAppVersion.text = String.format(getString(R.string.version_name_debug), BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ class EmptyWalletFragment : BaseFragment(), NavigationResultListener,
|
|||
val isoDep = IsoDep.get(tag)
|
||||
val uid = tag.id
|
||||
val sUID = Util.byteArrayToHexString(uid)
|
||||
if (ctx.card.uid != sUID) {
|
||||
if (ctx.card.uid != sUID || cardProtocol != null) {
|
||||
(activity as MainActivity).nfcManager.ignoreTag(isoDep.tag)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<BlockchainInfoAddressAndUnspents> addressAndUnspentsObserver = new DisposableSingleObserver<BlockchainInfoAddressAndUnspents>() {
|
||||
@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<ResponseBody> responseObserver = new DisposableSingleObserver<ResponseBody>() {
|
||||
@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();
|
||||
|
|
|
|||
|
|
@ -2,24 +2,37 @@ 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;
|
||||
import com.tangem.wallet.BTCUtils;
|
||||
import com.tangem.wallet.BalanceValidator;
|
||||
import com.tangem.wallet.Base58;
|
||||
import com.tangem.wallet.CoinData;
|
||||
import com.tangem.wallet.R;
|
||||
import com.tangem.wallet.TangemContext;
|
||||
import com.tangem.wallet.Transaction;
|
||||
import com.tangem.wallet.UnspentOutputInfo;
|
||||
import com.tangem.wallet.btc.BtcData;
|
||||
import com.tangem.wallet.btc.BtcEngine;
|
||||
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.util.CryptoUtil;
|
||||
import com.tangem.util.DecimalDigitsInputFilter;
|
||||
import com.tangem.util.DerEncodingUtil;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ import com.tangem.card_common.tasks.SignTask;
|
|||
import com.tangem.card_common.util.Util;
|
||||
import com.tangem.data.network.ServerApiRipple;
|
||||
import com.tangem.data.network.model.RippleResponse;
|
||||
import com.tangem.util.CryptoUtil;
|
||||
import com.tangem.util.DecimalDigitsInputFilter;
|
||||
import com.tangem.wallet.BTCUtils;
|
||||
import com.tangem.wallet.BalanceValidator;
|
||||
import com.tangem.wallet.CoinData;
|
||||
import com.tangem.wallet.CoinEngine;
|
||||
import com.tangem.wallet.TangemContext;
|
||||
import com.tangem.util.CryptoUtil;
|
||||
import com.tangem.util.DecimalDigitsInputFilter;
|
||||
import com.tangem.wallet.R;
|
||||
import com.tangem.wallet.TangemContext;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
|
@ -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())) {
|
||||
|
|
|
|||
BIN
app/src/main/res/drawable/ic_launcher_background.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
|
|
@ -1,4 +1,5 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/clLogoContainer"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
android:fontFamily="@font/maax"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -46,7 +47,7 @@
|
|||
android:text="@string/beta"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textColor="@android:color/holo_red_light"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.2 KiB |
|
|
@ -102,8 +102,8 @@
|
|||
<string name="erase_wallet">Erase wallet</string>
|
||||
|
||||
<!-- LogoActivity -->
|
||||
<string name="version_name_debug">BETA v.%1$s \n dev \n build %2$s</string>
|
||||
<string name="version_name_release">BETA v.%1$s</string>
|
||||
<string name="version_name_debug">ALPHA v.%1$s \n dev build %2$s</string>
|
||||
<string name="version_name_release">v.%1$s</string>
|
||||
|
||||
<string name="welcome_messages">Hello, %1$s! You have %2$d new messages.</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ buildscript {
|
|||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
}
|
||||
dependencies {
|
||||
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'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
compileOptions {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
----
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ android {
|
|||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug_beta {
|
||||
initWith debug
|
||||
versionNameSuffix "-beta"
|
||||
}
|
||||
}
|
||||
productFlavors {
|
||||
}
|
||||
|
|
|
|||