From 96a450e0e57780a6256012327f9710c747544c7e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 May 2023 16:28:00 +0300 Subject: [PATCH 01/32] Updated on 2026-08-14 --- .../tap/features/wallet/ui/WalletFragment.kt | 10 -- core/datasource/build.gradle.kts | 2 - core/datasource/src/main/AndroidManifest.xml | 3 +- .../AndroidNetworkConnectionManager.kt | 110 ++++++++++++++++++ .../RealInternetConnectionManager.kt | 29 ----- .../datasource/di/NetworkConnectionModule.kt | 14 +-- gradle/dependencies.toml | 3 - 7 files changed, 118 insertions(+), 53 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index be2387002a..4522299310 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -279,16 +279,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber - if (isOnline) { - (activity as? MainActivity)?.dismissSnackbar() - } else { - isNetworkConnectionError.value = true - binding.srlWallet.isRefreshing = false - (activity as? MainActivity)?.showSnackbar( - text = R.string.wallet_notification_no_internet, - buttonTitle = R.string.common_retry, - ) - } if (isOnline && isNetworkConnectionError.value) { refreshWalletData() } diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 3350c7930f..36c8fda7a2 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -22,7 +22,6 @@ dependencies { /** Coroutines */ implementation(deps.kotlin.coroutines) - implementation(deps.kotlin.coroutines.reactive) /** Logging */ implementation(deps.timber) @@ -35,7 +34,6 @@ dependencies { implementation(deps.okHttp.logging) implementation(deps.retrofit) implementation(deps.retrofit.moshi) - implementation(deps.reactive.network) /** Time */ implementation(deps.jodatime) diff --git a/core/datasource/src/main/AndroidManifest.xml b/core/datasource/src/main/AndroidManifest.xml index a87322003a..b7e7790e14 100644 --- a/core/datasource/src/main/AndroidManifest.xml +++ b/core/datasource/src/main/AndroidManifest.xml @@ -3,5 +3,4 @@ xmlns:android="http://schemas.android.com/apk/res/android"> - - + \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt new file mode 100644 index 0000000000..6581fbb384 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt @@ -0,0 +1,110 @@ +package com.tangem.datasource.connection + +import android.app.Activity +import android.app.Application +import android.app.Application.ActivityLifecycleCallbacks +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.NetworkCapabilities +import android.os.Build +import android.os.Bundle +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton +import android.net.ConnectivityManager as AndroidConnectivityManager + +/** + * Network connection manager implementation using Android connectivity service + * + * @property context application context for registration lifecycle callbacks + * @property dispatchers coroutine dispatcher provider + * +[REDACTED_AUTHOR] + */ +@Singleton +internal class AndroidNetworkConnectionManager @Inject constructor( + @ApplicationContext private val context: Context, + private val dispatchers: CoroutineDispatcherProvider, +) : NetworkConnectionManager { + + private val _isOnline = MutableStateFlow(value = false) + private val callbacks = NetworkConnectionManagerCallbacks() + private val receiver = NetworkConnectionBroadcastReceiver() + + override val isOnline: Boolean get() = _isOnline.value + override val isOnlineFlow: StateFlow = _isOnline + + init { + (context as? Application)?.registerActivityLifecycleCallbacks(callbacks) + } + + private inner class NetworkConnectionManagerCallbacks : ActivityLifecycleCallbacks { + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + val intentFilter = IntentFilter().apply { + addAction(AndroidConnectivityManager.CONNECTIVITY_ACTION) + } + + activity.registerReceiver(receiver, intentFilter) + } + + override fun onActivityStarted(activity: Activity) = Unit + override fun onActivityResumed(activity: Activity) = Unit + override fun onActivityPaused(activity: Activity) = Unit + override fun onActivityStopped(activity: Activity) = Unit + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit + + override fun onActivityDestroyed(activity: Activity) { + activity.unregisterReceiver(receiver) + (context as? Application)?.unregisterActivityLifecycleCallbacks(callbacks) + } + } + + private inner class NetworkConnectionBroadcastReceiver : BroadcastReceiver() { + + private val connectivityManager: android.net.ConnectivityManager? = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as? android.net.ConnectivityManager + + override fun onReceive(c: Context?, intent: Intent?) { + val pendingResult = goAsync() + + CoroutineScope(SupervisorJob()).launch(dispatchers.io) { + try { + val status = isConnected() + + _isOnline.emit(value = status) + Timber.i("Connection status is ${if (status) "ONLINE" else "OFFLINE"}") + } finally { + pendingResult.finish() + } + } + } + + private fun isConnected(): Boolean { + connectivityManager ?: return false + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + capabilities != null && capabilities.hasNetworkTransport() + } else { + val networkInfo = connectivityManager.activeNetworkInfo + networkInfo != null && networkInfo.isConnectedOrConnecting + } + } + + private fun NetworkCapabilities.hasNetworkTransport(): Boolean { + return hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || + hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || + hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) + } + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt deleted file mode 100644 index 669dc8ed20..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.datasource.connection - -import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork -import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler -import io.reactivex.BackpressureStrategy -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.reactive.asFlow - -internal class RealInternetConnectionManager : NetworkConnectionManager { - - private val scope = - CoroutineScope( - SupervisorJob() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("RealInternetConnectionManager"), - ) - - override val isOnlineFlow: StateFlow = ReactiveNetwork - .observeInternetConnectivity() - .toFlowable(BackpressureStrategy.LATEST) - .asFlow() - .stateIn(scope, SharingStarted.Eagerly, initialValue = false) - - override val isOnline: Boolean - get() = isOnlineFlow.value -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt index b3eca89707..d112c7ccab 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt @@ -1,20 +1,20 @@ package com.tangem.datasource.di +import com.tangem.datasource.connection.AndroidNetworkConnectionManager import com.tangem.datasource.connection.NetworkConnectionManager -import com.tangem.datasource.connection.RealInternetConnectionManager +import dagger.Binds import dagger.Module -import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal class NetworkConnectionModule { +internal interface NetworkConnectionModule { - @Provides + @Binds @Singleton - fun provideNetworkConnectionManager(): NetworkConnectionManager { - return RealInternetConnectionManager() - } + fun bindNetworkConnectionManager( + androidNetworkConnectionManager: AndroidNetworkConnectionManager, + ): NetworkConnectionManager } \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 452d9dcf83..860b2162f4 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -69,7 +69,6 @@ zxingQrCode = "3.5.1" mviCore = "1.3.1" kotlinSerialization = "1.4.1" arrow = "1.2.0-RC" -reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem @@ -175,7 +174,6 @@ appsflyer = { module = "com.appsflyer:af-android-sdk", version.ref = "appsflyer" armadillo = { module = "at.favre.lib:armadillo", version.ref = "armadillo" } coil = { module = "io.coil-kt:coil", version.ref = "coil" } kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutine" } -kotlin-coroutines-reactive = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactive", version.ref = "coroutine" } kotlin-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlin-immutable-collections" } desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" } googlePlay-core = { module = "com.google.android.play:core", version.ref = "googlePlayCore" } @@ -211,5 +209,4 @@ mviCore-watcher = { module = "com.github.badoo.mvicore:mvicore-diff", version.re kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" } arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } arrow-fx = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" } -reactive-network = { module = "com.github.pwittchen:reactivenetwork-rx2", version.ref = "reactiveNetwork" } # endregion Other From 91ba31fb84cd82574e82b2907b7f47167c3518a5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 May 2023 16:50:09 +0300 Subject: [PATCH 02/32] Updated on 2026-08-14 --- .../tap/features/wallet/ui/WalletFragment.kt | 10 ++ core/datasource/build.gradle.kts | 2 + core/datasource/src/main/AndroidManifest.xml | 3 +- .../AndroidNetworkConnectionManager.kt | 110 ------------------ .../RealInternetConnectionManager.kt | 29 +++++ .../datasource/di/NetworkConnectionModule.kt | 14 +-- gradle/dependencies.toml | 3 + 7 files changed, 53 insertions(+), 118 deletions(-) delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 4522299310..be2387002a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -279,6 +279,16 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber + if (isOnline) { + (activity as? MainActivity)?.dismissSnackbar() + } else { + isNetworkConnectionError.value = true + binding.srlWallet.isRefreshing = false + (activity as? MainActivity)?.showSnackbar( + text = R.string.wallet_notification_no_internet, + buttonTitle = R.string.common_retry, + ) + } if (isOnline && isNetworkConnectionError.value) { refreshWalletData() } diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 36c8fda7a2..3350c7930f 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { /** Coroutines */ implementation(deps.kotlin.coroutines) + implementation(deps.kotlin.coroutines.reactive) /** Logging */ implementation(deps.timber) @@ -34,6 +35,7 @@ dependencies { implementation(deps.okHttp.logging) implementation(deps.retrofit) implementation(deps.retrofit.moshi) + implementation(deps.reactive.network) /** Time */ implementation(deps.jodatime) diff --git a/core/datasource/src/main/AndroidManifest.xml b/core/datasource/src/main/AndroidManifest.xml index b7e7790e14..a87322003a 100644 --- a/core/datasource/src/main/AndroidManifest.xml +++ b/core/datasource/src/main/AndroidManifest.xml @@ -3,4 +3,5 @@ xmlns:android="http://schemas.android.com/apk/res/android"> - \ No newline at end of file + + diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt deleted file mode 100644 index 6581fbb384..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/connection/AndroidNetworkConnectionManager.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.tangem.datasource.connection - -import android.app.Activity -import android.app.Application -import android.app.Application.ActivityLifecycleCallbacks -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.net.NetworkCapabilities -import android.os.Build -import android.os.Bundle -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Singleton -import android.net.ConnectivityManager as AndroidConnectivityManager - -/** - * Network connection manager implementation using Android connectivity service - * - * @property context application context for registration lifecycle callbacks - * @property dispatchers coroutine dispatcher provider - * -[REDACTED_AUTHOR] - */ -@Singleton -internal class AndroidNetworkConnectionManager @Inject constructor( - @ApplicationContext private val context: Context, - private val dispatchers: CoroutineDispatcherProvider, -) : NetworkConnectionManager { - - private val _isOnline = MutableStateFlow(value = false) - private val callbacks = NetworkConnectionManagerCallbacks() - private val receiver = NetworkConnectionBroadcastReceiver() - - override val isOnline: Boolean get() = _isOnline.value - override val isOnlineFlow: StateFlow = _isOnline - - init { - (context as? Application)?.registerActivityLifecycleCallbacks(callbacks) - } - - private inner class NetworkConnectionManagerCallbacks : ActivityLifecycleCallbacks { - - override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { - val intentFilter = IntentFilter().apply { - addAction(AndroidConnectivityManager.CONNECTIVITY_ACTION) - } - - activity.registerReceiver(receiver, intentFilter) - } - - override fun onActivityStarted(activity: Activity) = Unit - override fun onActivityResumed(activity: Activity) = Unit - override fun onActivityPaused(activity: Activity) = Unit - override fun onActivityStopped(activity: Activity) = Unit - override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) = Unit - - override fun onActivityDestroyed(activity: Activity) { - activity.unregisterReceiver(receiver) - (context as? Application)?.unregisterActivityLifecycleCallbacks(callbacks) - } - } - - private inner class NetworkConnectionBroadcastReceiver : BroadcastReceiver() { - - private val connectivityManager: android.net.ConnectivityManager? = - context.getSystemService(Context.CONNECTIVITY_SERVICE) as? android.net.ConnectivityManager - - override fun onReceive(c: Context?, intent: Intent?) { - val pendingResult = goAsync() - - CoroutineScope(SupervisorJob()).launch(dispatchers.io) { - try { - val status = isConnected() - - _isOnline.emit(value = status) - Timber.i("Connection status is ${if (status) "ONLINE" else "OFFLINE"}") - } finally { - pendingResult.finish() - } - } - } - - private fun isConnected(): Boolean { - connectivityManager ?: return false - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) - capabilities != null && capabilities.hasNetworkTransport() - } else { - val networkInfo = connectivityManager.activeNetworkInfo - networkInfo != null && networkInfo.isConnectedOrConnecting - } - } - - private fun NetworkCapabilities.hasNetworkTransport(): Boolean { - return hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || - hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || - hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) - } - } -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt new file mode 100644 index 0000000000..669dc8ed20 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt @@ -0,0 +1,29 @@ +package com.tangem.datasource.connection + +import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler +import io.reactivex.BackpressureStrategy +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.reactive.asFlow + +internal class RealInternetConnectionManager : NetworkConnectionManager { + + private val scope = + CoroutineScope( + SupervisorJob() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("RealInternetConnectionManager"), + ) + + override val isOnlineFlow: StateFlow = ReactiveNetwork + .observeInternetConnectivity() + .toFlowable(BackpressureStrategy.LATEST) + .asFlow() + .stateIn(scope, SharingStarted.Eagerly, initialValue = false) + + override val isOnline: Boolean + get() = isOnlineFlow.value +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt index d112c7ccab..b3eca89707 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkConnectionModule.kt @@ -1,20 +1,20 @@ package com.tangem.datasource.di -import com.tangem.datasource.connection.AndroidNetworkConnectionManager import com.tangem.datasource.connection.NetworkConnectionManager -import dagger.Binds +import com.tangem.datasource.connection.RealInternetConnectionManager import dagger.Module +import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal interface NetworkConnectionModule { +internal class NetworkConnectionModule { - @Binds + @Provides @Singleton - fun bindNetworkConnectionManager( - androidNetworkConnectionManager: AndroidNetworkConnectionManager, - ): NetworkConnectionManager + fun provideNetworkConnectionManager(): NetworkConnectionManager { + return RealInternetConnectionManager() + } } \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 860b2162f4..452d9dcf83 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -69,6 +69,7 @@ zxingQrCode = "3.5.1" mviCore = "1.3.1" kotlinSerialization = "1.4.1" arrow = "1.2.0-RC" +reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem @@ -174,6 +175,7 @@ appsflyer = { module = "com.appsflyer:af-android-sdk", version.ref = "appsflyer" armadillo = { module = "at.favre.lib:armadillo", version.ref = "armadillo" } coil = { module = "io.coil-kt:coil", version.ref = "coil" } kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutine" } +kotlin-coroutines-reactive = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactive", version.ref = "coroutine" } kotlin-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlin-immutable-collections" } desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" } googlePlay-core = { module = "com.google.android.play:core", version.ref = "googlePlayCore" } @@ -209,4 +211,5 @@ mviCore-watcher = { module = "com.github.badoo.mvicore:mvicore-diff", version.re kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" } arrow-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } arrow-fx = { module = "io.arrow-kt:arrow-fx-coroutines", version.ref = "arrow" } +reactive-network = { module = "com.github.pwittchen:reactivenetwork-rx2", version.ref = "reactiveNetwork" } # endregion Other From f0211b94728a3ef992f54362b4bd2efafdb428b2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 May 2023 16:52:00 +0300 Subject: [PATCH 03/32] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7e632399d0..ade65672ac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -35,6 +35,7 @@ android:supportsRtl="true" android:allowBackup="false" android:networkSecurityConfig="@xml/network_security_config" + android:usesCleartextTraffic="true" android:hardwareAccelerated="true" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:allowBackup"> From 2b3f6360e33319d06f895b331a0d06f2860c98b8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 May 2023 19:42:02 +0500 Subject: [PATCH 04/32] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 1 - app/src/main/res/xml/network_security_config.xml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ade65672ac..7e632399d0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -35,7 +35,6 @@ android:supportsRtl="true" android:allowBackup="false" android:networkSecurityConfig="@xml/network_security_config" - android:usesCleartextTraffic="true" android:hardwareAccelerated="true" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:allowBackup"> diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index 454c57b5fd..22e9f8653e 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -1,5 +1,6 @@ + api.tangem.com @@ -7,6 +8,4 @@ - - From 7ddea4dbd83a28f0df3a2059a5707d2aefd3cbba Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 24 May 2023 15:01:58 +0300 Subject: [PATCH 05/32] Updated on 2026-08-14 --- .../tap/features/shop/ui/ShopFragment.kt | 58 +++- app/src/main/res/layout/fragment_shop.xml | 257 ++++++++---------- core/res/src/main/res/values-ru/strings.xml | 4 +- .../src/main/res/values-zh-rTW/strings.xml | 2 - core/res/src/main/res/values/strings.xml | 4 +- 5 files changed, 169 insertions(+), 156 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt index dccbe4d7b7..ad760cf53b 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt @@ -4,6 +4,9 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.content.Context import android.os.Bundle +import android.text.Spannable +import android.text.SpannableString +import android.text.style.ForegroundColorSpan import android.view.View import android.view.View.OnFocusChangeListener import android.view.inputmethod.EditorInfo @@ -12,7 +15,9 @@ import androidx.activity.OnBackPressedCallback import by.kirich1409.viewbindingdelegate.viewBinding import com.tangem.tap.common.GlobalLayoutStateHandler import com.tangem.tap.common.KeyboardObserver +import com.tangem.tap.common.extensions.getColorCompat import com.tangem.tap.common.extensions.getQuantityString +import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.shop.data.ProductType @@ -23,14 +28,27 @@ import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.FragmentShopBinding import org.rekotlin.StoreSubscriber +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber { + private val dateFormat = SimpleDateFormat("dd MMMM", Locale.getDefault()) private val binding: FragmentShopBinding by viewBinding(FragmentShopBinding::bind) + private val endPreorderTime = Calendar.getInstance() + private val isBuyAvailable: Boolean + private var cardTranslationY = 70f private lateinit var keyboardObserver: KeyboardObserver + init { + endPreorderTime.clear() + endPreorderTime.set(END_DATE_SOLD_OUT_YEAR, END_DATE_SOLD_OUT_MONTH, END_DATE_SOLD_OUT_DAY) + isBuyAvailable = System.currentTimeMillis() > endPreorderTime.timeInMillis + } + override fun subscribeToStore() { store.subscribe(this) { state -> state.skipRepeats { oldState, newState -> @@ -65,6 +83,7 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber< setupCardsImages() setupProductSelection() setupPromoCodeEditText() + setupSoldOutDescription() binding.toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() @@ -127,6 +146,26 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber< } } + private fun setupSoldOutDescription() = with(binding) { + context?.let { cntx -> + if (!isBuyAvailable) { + val date = "${dateFormat.format(endPreorderTime.time)}!" + val soldOutTitle = cntx.getString(R.string.shop_sold_out_description_prefix) + val generalString = "$soldOutTitle $date" + val wordToSpan: Spannable = SpannableString(generalString) + wordToSpan.setSpan( + ForegroundColorSpan(cntx.getColorCompat(R.color.textBlack)), + generalString.length - date.length, + generalString.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, + ) + tvSoldOutDesc.text = wordToSpan + } else { + tvSoldOutDesc.hide() + } + } + } + override fun newState(state: ShopState) { if (activity == null || view == null) return @@ -174,9 +213,16 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber< } private fun handleButtonsState(state: ShopState) = with(binding) { - btnPayGooglePay.root.show(state.isGooglePayAvailable) - btnAlternativePayment.show(state.isGooglePayAvailable) - btnMainAction.show(!state.isGooglePayAvailable) + if (isBuyAvailable) { + btnPayGooglePay.root.show(state.isGooglePayAvailable) + btnAlternativePayment.show(state.isGooglePayAvailable) + btnMainAction.show(!state.isGooglePayAvailable) + } else { + btnPayGooglePay.root.hide() + btnAlternativePayment.hide() + btnMainAction.show() + btnMainAction.text = context?.getString(R.string.shop_pre_order_now) + } if (state.total != null) { btnAlternativePayment.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) } @@ -189,4 +235,10 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber< store.dispatch(ShopAction.ResetState) super.handleOnBackPressed() } + + companion object { + private const val END_DATE_SOLD_OUT_YEAR = 2023 + private const val END_DATE_SOLD_OUT_MONTH = 5 // June, month start counting from zero! + private const val END_DATE_SOLD_OUT_DAY = 10 + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_shop.xml b/app/src/main/res/layout/fragment_shop.xml index c3dadcdb4b..1e86e09354 100644 --- a/app/src/main/res/layout/fragment_shop.xml +++ b/app/src/main/res/layout/fragment_shop.xml @@ -132,7 +132,7 @@ android:paddingStart="3dp" android:paddingTop="3dp" android:paddingEnd="3dp" - app:layout_constraintBottom_toTopOf="@id/v_shipping_discount_background" + app:layout_constraintBottom_toTopOf="@id/ll_total" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:selectionRequired="true" @@ -156,162 +156,125 @@ app:chipMinTouchTargetSize="0dp" /> - - - - - - - - - - - - - - - - - - + android:orientation="vertical" + app:layout_constraintBottom_toTopOf="@id/ll_buttons"> - + - + - + - + + + + + + + + + + + + + + + + + Транзакция успешно подписана и отправлена в блокчейн. Баланс будет обновлен через некоторое время Неверный адрес Купить сейчас - Бесплатно У меня есть промо-код… Tangem Wallet Другие способы оплаты - Доставка + Сделать предзаказ + Все распродано, но не огорчайтесь! Мы уже производим новые партии Tangem Wallet. Чтобы получить ваш кошелек скорее, оформите предзаказ сейчас. Отправка заказов начнется Итого Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату. Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index 648337e245..edfc9bc0cc 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -330,11 +330,9 @@ 交易已成功簽署並發送至區塊鏈節點。錢包餘額稍後更新 無效地址 現在購買 - 免費 輸入折扣碼 Tangem Wallet 其他付款方式 - 運送 總計 Solana 網絡每 2 天收取 %1$s 的費用。無法付此費用的帳戶將從網絡中清除。向您的帳戶存入超過 %2$s 即可免費使用 安全地存儲您的加密貨幣,同時將私鑰保存在您的卡中 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 0bf985e22a..37b2deb719 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -338,11 +338,11 @@ Transaction has been successfully signed and sent to the blockchain node. Wallet balance will be updated in a while Invalid address Buy now - Free I have a promo code… Tangem Wallet Other payment methods - Shipping + Pre-order now + Sold out, but don\'t worry! We are working very hard to get more for you! Make sure to place a pre-order here. Deliveries start on Total Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free. Store your crypto assets secure while keeping private keys contained in your card From 240ce4d198c1202673bcd779d0378abdd31bbecf Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 29 May 2023 15:13:08 +0300 Subject: [PATCH 06/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 1ad8e1ccca..2d33326ff2 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,9 +73,9 @@ reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-225" +tangemBlockchainSdk = "release-app_4.6-227" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-249" +tangemCardSdk = "release-app_4.6-251" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem From c908c05fac269c990ea69401f8e3e1c30c8f5e27 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 29 May 2023 15:36:24 +0300 Subject: [PATCH 07/32] Updated on 2026-08-14 --- app/src/main/res/layout/fragment_shop.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/layout/fragment_shop.xml b/app/src/main/res/layout/fragment_shop.xml index 1e86e09354..523b68c535 100644 --- a/app/src/main/res/layout/fragment_shop.xml +++ b/app/src/main/res/layout/fragment_shop.xml @@ -168,7 +168,7 @@ From a6e38a0284372f4ed685a64b83e9def1b73f0b49 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 30 May 2023 21:25:18 +0500 Subject: [PATCH 08/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 2d33326ff2..52c9d2906f 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,7 +73,7 @@ reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.6-227" +tangemBlockchainSdk = "release-app_4.6-229" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_4.6-251" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From 537922e9db65af38ddef75951462161f236dffd3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 10:35:16 +0300 Subject: [PATCH 09/32] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 48 +++++++++++--------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7e632399d0..b9fd68f4ee 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,6 +49,7 @@ android:launchMode="singleTop" android:screenOrientation="portrait" android:windowSoftInputMode="adjustResize"> + @@ -58,40 +59,25 @@ - - - - - - + + + - - - - + + + - + + + - - - + + + + + + + From 7a177e3467e578bb8e2a64cd73e230ba2f7a1dde Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 09:58:40 +0300 Subject: [PATCH 10/32] Updated on 2026-08-14 --- .../common/analytics/events/AnalyticsParam.kt | 50 ++++++++++++- .../tap/common/analytics/events/Basic.kt | 12 +++- .../send/redux/middlewares/SendMiddleware.kt | 72 ++++++++++--------- .../features/send/redux/states/FeeState.kt | 12 +++- .../tap/proxy/TransactionManagerImpl.kt | 70 +++++++++++------- .../feature/swap/domain/SwapInteractorImpl.kt | 46 +++++++----- .../domain/models/domain/SwapApproveType.kt | 7 ++ .../swap/domain/models/ui/SwapState.kt | 14 +++- .../feature/swap/ui/SwapScreenContent.kt | 4 ++ .../tangem/lib/crypto/TransactionManager.kt | 29 ++++---- .../tangem/lib/crypto/models/AnalyticsData.kt | 14 ++++ .../tangem/lib/crypto/models/ApproveTxData.kt | 20 ++++++ .../tangem/lib/crypto/models/SwapTxData.kt | 24 +++++++ 13 files changed, 274 insertions(+), 100 deletions(-) create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt index 15c29ad5d0..7a788fd5c6 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt @@ -87,12 +87,54 @@ sealed class AnalyticsParam { } sealed class TxSentFrom(val value: String) { - object Send : TxSentFrom("Send") - object Swap : TxSentFrom("Swap") + data class Send( + override val blockchain: String, + override val token: String, + override val feeType: FeeType, + ) : TxSentFrom("Send"), TxData + + data class Swap( + override val blockchain: String, + override val token: String, + override val feeType: FeeType, + ) : TxSentFrom("Swap"), TxData + + data class Approve( + override val blockchain: String, + override val token: String, + override val feeType: FeeType, + val permissionType: String, + ) : TxSentFrom("Approve"), TxData + object WalletConnect : TxSentFrom("WalletConnect") object Sell : TxSentFrom("Sell") } + sealed interface TxData { + val blockchain: String + val token: String + val feeType: FeeType + } + + sealed class FeeType(val value: String) { + object Fixed : FeeType("Fixed") + object Min : FeeType("Min") + object Normal : FeeType("Normal") + object Max : FeeType("Max") + + companion object { + fun fromString(feeType: String): FeeType { + return when (feeType) { + Min.value -> Min + Normal.value -> Normal + Max.value -> Max + Fixed.value -> Fixed + else -> Fixed + } + } + } + } + sealed class WalletCreationType(val value: String) { object PrivateKey : WalletCreationType("Private key") object NewSeed : WalletCreationType("New seed") @@ -100,9 +142,13 @@ sealed class AnalyticsParam { } companion object Key { + const val BLOCKCHAIN = "blockchain" + const val TOKEN = "Token" const val SOURCE = "Source" const val BALANCE = "Balance" const val BATCH = "Batch" + const val FEE_TYPE = "Fee Type" + const val PERMISSION_TYPE = "Permission Type" const val PRODUCT_TYPE = "Product Type" const val FIRMWARE = "Firmware" const val CURRENCY = "Currency" diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt index 4f63bd4961..d9b029fcae 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt @@ -51,7 +51,17 @@ sealed class Basic( class TransactionSent(sentFrom: AnalyticsParam.TxSentFrom) : Basic( event = "Transaction sent", - params = mapOf(AnalyticsParam.SOURCE to sentFrom.value), + params = buildMap { + this[AnalyticsParam.SOURCE] = sentFrom.value + if (sentFrom is AnalyticsParam.TxData) { + this[AnalyticsParam.BLOCKCHAIN] = sentFrom.blockchain + this[AnalyticsParam.TOKEN] = sentFrom.token + this[AnalyticsParam.FEE_TYPE] = sentFrom.feeType.value + } + if (sentFrom is AnalyticsParam.TxSentFrom.Approve) { + this[AnalyticsParam.PERMISSION_TYPE] = sentFrom.permissionType + } + }, ) class ScanError(error: Throwable) : Basic( diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 2d22340b26..dbc8be67c1 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -7,28 +7,20 @@ import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.ton.TonTransactionExtras import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.TransactionError -import com.tangem.blockchain.common.TransactionSender -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.* import com.tangem.blockchain.extensions.SimpleResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard import com.tangem.common.services.Result import com.tangem.core.analytics.Analytics -import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.extensions.withMainContext -import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE +import com.tangem.domain.models.scan.CardDTO +import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchErrorNotification -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.safeUpdate -import com.tangem.tap.common.extensions.stripZeroPlainString +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -39,25 +31,11 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.minimalAmount import com.tangem.tap.features.demo.DemoTransactionSender import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.send.redux.AddressPayIdActionUi -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction -import com.tangem.tap.features.send.redux.AmountAction -import com.tangem.tap.features.send.redux.AmountActionUi +import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee -import com.tangem.tap.features.send.redux.PrepareSendScreen -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.features.send.redux.SendActionUi -import com.tangem.tap.features.send.redux.states.ButtonState -import com.tangem.tap.features.send.redux.states.ExternalTransactionData -import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.TransactionExtrasState +import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.tangemSdk -import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletCurrenciesManager import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -65,7 +43,7 @@ import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware import timber.log.Timber -import java.util.* +import java.util.EnumSet /** [REDACTED_AUTHOR] @@ -134,9 +112,16 @@ private fun verifyAndSendTransaction( }, sendAllCallback = { sendTransaction( - action, walletManager, amountToSend, feeAmount, destinationAddress, - sendState.transactionExtrasState, card, sendState.externalTransactionData, - dispatch, + action = action, + walletManager = walletManager, + amountToSend = amountToSend, + feeAmount = feeAmount, + feeType = sendState.feeState.selectedFeeType, + destinationAddress = destinationAddress, + transactionExtras = sendState.transactionExtrasState, + card = card, + externalTransactionData = sendState.externalTransactionData, + dispatch = dispatch, ) }, reduceAmount, @@ -145,8 +130,16 @@ private fun verifyAndSendTransaction( } else -> { sendTransaction( - action, walletManager, amountToSend, feeAmount, destinationAddress, - sendState.transactionExtrasState, card, sendState.externalTransactionData, dispatch, + action = action, + walletManager = walletManager, + amountToSend = amountToSend, + feeAmount = feeAmount, + feeType = sendState.feeState.selectedFeeType, + destinationAddress = destinationAddress, + transactionExtras = sendState.transactionExtrasState, + card = card, + externalTransactionData = sendState.externalTransactionData, + dispatch = dispatch, ) } } @@ -158,6 +151,7 @@ private fun sendTransaction( walletManager: WalletManager, amountToSend: Amount, feeAmount: Amount, + feeType: FeeType, destinationAddress: String, transactionExtras: TransactionExtrasState, card: CardDTO, @@ -253,7 +247,15 @@ private fun sendTransaction( Analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Sell)) dispatch(WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)) } else { - Analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Send)) + Analytics.send( + Basic.TransactionSent( + sentFrom = AnalyticsParam.TxSentFrom.Send( + blockchain = walletManager.wallet.blockchain.fullName, + token = amountToSend.currencySymbol, + feeType = feeType.convertToAnalyticsFeeType(), + ), + ), + ) dispatch(NavigationAction.PopBackTo()) } scope.launch(Dispatchers.IO) { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt index e9c9472bb4..65067fc0f8 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.send.redux.states import com.tangem.blockchain.common.Amount +import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.features.wallet.redux.ProgressState import java.math.BigDecimal @@ -8,7 +9,16 @@ import java.math.BigDecimal [REDACTED_AUTHOR] */ enum class FeeType { - SINGLE, LOW, NORMAL, PRIORITY + SINGLE, LOW, NORMAL, PRIORITY; +} + +fun FeeType.convertToAnalyticsFeeType(): AnalyticsParam.FeeType { + return when (this) { + FeeType.SINGLE -> AnalyticsParam.FeeType.Fixed + FeeType.LOW -> AnalyticsParam.FeeType.Min + FeeType.NORMAL -> AnalyticsParam.FeeType.Normal + FeeType.PRIORITY -> AnalyticsParam.FeeType.Max + } } data class FeeState( diff --git a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt index dabd3c53ec..216203fce9 100644 --- a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -34,14 +34,11 @@ class TransactionManagerImpl( ) : TransactionManager { override suspend fun sendApproveTransaction( - networkId: String, - feeAmount: BigDecimal, - gasLimit: Int, - destinationAddress: String, - dataToSign: String, + txData: ApproveTxData, derivationPath: String?, + analyticsData: AnalyticsData, ): SendTxResult { - val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } + val blockchain = requireNotNull(Blockchain.fromNetworkId(txData.networkId)) { "blockchain not found" } val walletManager = getActualWalletManager(blockchain, derivationPath) walletManager.update() val amount = Amount(value = BigDecimal.ZERO, blockchain = blockchain) @@ -49,40 +46,55 @@ class TransactionManagerImpl( walletManager = walletManager, amount = amount, blockchain = blockchain, - feeAmount = feeAmount, - gasLimit = gasLimit, - destinationAddress = destinationAddress, - dataToSign = dataToSign, + feeAmount = txData.feeAmount, + gasLimit = txData.gasLimit, + destinationAddress = txData.destinationAddress, + dataToSign = txData.dataToSign, + successEvent = AnalyticsParam.TxSentFrom.Approve( + blockchain = blockchain.fullName, + token = analyticsData.tokenSymbol, + feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), + permissionType = analyticsData.permissionType ?: "", + ), ) } override suspend fun sendTransaction( - networkId: String, - amountToSend: BigDecimal, - feeAmount: BigDecimal, - gasLimit: Int, - destinationAddress: String, - dataToSign: String, + txData: SwapTxData, isSwap: Boolean, - currencyToSend: Currency, derivationPath: String?, + analyticsData: AnalyticsData, ): SendTxResult { - val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } + val blockchain = requireNotNull(Blockchain.fromNetworkId(txData.networkId)) { "blockchain not found" } val walletManager = getActualWalletManager(blockchain, derivationPath) walletManager.update() val amount = if (isSwap) { - createAmountForSwap(amountToSend, currencyToSend, blockchain) + createAmountForSwap(txData.amountToSend, txData.currencyToSend, blockchain) } else { - createAmount(amountToSend, currencyToSend, blockchain) + createAmount(txData.amountToSend, txData.currencyToSend, blockchain) + } + val successEvent = if (isSwap) { + AnalyticsParam.TxSentFrom.Swap( + blockchain = blockchain.fullName, + token = analyticsData.tokenSymbol, + feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), + ) + } else { + AnalyticsParam.TxSentFrom.Send( + blockchain = blockchain.fullName, + token = analyticsData.tokenSymbol, + feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), + ) } return sendTransactionInternal( walletManager = walletManager, amount = amount, blockchain = blockchain, - feeAmount = feeAmount, - gasLimit = gasLimit, - destinationAddress = destinationAddress, - dataToSign = dataToSign, + feeAmount = txData.feeAmount, + gasLimit = txData.gasLimit, + destinationAddress = txData.destinationAddress, + dataToSign = txData.dataToSign, + successEvent = successEvent, ) } @@ -95,6 +107,7 @@ class TransactionManagerImpl( gasLimit: Int, destinationAddress: String, dataToSign: String, + successEvent: AnalyticsParam.TxSentFrom, ): SendTxResult { val txData = walletManager.createTransaction( amount = amount, @@ -110,7 +123,10 @@ class TransactionManagerImpl( FirebaseCrashlytics.getInstance().recordException(ex) return SendTxResult.UnknownError(ex) } - return handleSendResult(sendResult) + return handleSendResult( + result = sendResult, + successEvent = successEvent, + ) } override fun getExplorerTransactionLink(networkId: String, txAddress: String): String { @@ -319,10 +335,10 @@ class TransactionManagerImpl( } } - private fun handleSendResult(result: SimpleResult): SendTxResult { + private fun handleSendResult(result: SimpleResult, successEvent: AnalyticsParam.TxSentFrom): SendTxResult { when (result) { is SimpleResult.Success -> { - analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Swap)) + analytics.send(Basic.TransactionSent(sentFrom = successEvent)) return SendTxResult.Success } is SimpleResult.Failure -> { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 835ebdf273..25a3e74cf3 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -4,16 +4,13 @@ import com.tangem.feature.swap.domain.cache.SwapDataCache import com.tangem.feature.swap.domain.converters.CryptoCurrencyConverter import com.tangem.feature.swap.domain.models.DataError import com.tangem.feature.swap.domain.models.SwapAmount +import com.tangem.feature.swap.domain.models.domain.* import com.tangem.feature.swap.domain.models.domain.Currency -import com.tangem.feature.swap.domain.models.domain.PermissionOptions -import com.tangem.feature.swap.domain.models.domain.PreparedSwapConfigState -import com.tangem.feature.swap.domain.models.domain.SwapApproveType import com.tangem.feature.swap.domain.models.toStringWithRightOffset import com.tangem.feature.swap.domain.models.ui.* import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager -import com.tangem.lib.crypto.models.ProxyFees -import com.tangem.lib.crypto.models.ProxyFiatCurrency +import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult import com.tangem.utils.toFiatString import java.math.BigDecimal @@ -115,12 +112,19 @@ internal class SwapInteractorImpl @Inject constructor( permissionOptions.approveData.approveModel.data } val result = transactionManager.sendApproveTransaction( - networkId = networkId, - feeAmount = permissionOptions.txFee.feeValue, - gasLimit = permissionOptions.txFee.gasLimit, - destinationAddress = permissionOptions.approveData.approveModel.toAddress, - dataToSign = dataToSign, + txData = ApproveTxData( + networkId = networkId, + feeAmount = permissionOptions.txFee.feeValue, + gasLimit = permissionOptions.txFee.gasLimit, + destinationAddress = permissionOptions.approveData.approveModel.toAddress, + dataToSign = dataToSign, + ), derivationPath = derivationPath, + analyticsData = AnalyticsData( + feeType = permissionOptions.txFee.feeType.getNameForAnalytics(), + tokenSymbol = permissionOptions.fromToken.symbol, + permissionType = permissionOptions.approveType.getNameForAnalytics(), + ), ) return when (result) { is SendTxResult.Success -> { @@ -188,15 +192,21 @@ internal class SwapInteractorImpl @Inject constructor( ): TxState { val amount = requireNotNull(toBigDecimalOrNull(amountToSwap)) { "wrong amount format, use only digits" } val result = transactionManager.sendTransaction( - networkId = networkId, - amountToSend = amount, - currencyToSend = cryptoCurrencyConverter.convert(currencyToSend), - feeAmount = fee.feeValue, - gasLimit = fee.gasLimit, - destinationAddress = swapStateData.swapModel.transaction.toWalletAddress, - dataToSign = swapStateData.swapModel.transaction.data, + txData = SwapTxData( + networkId = networkId, + amountToSend = amount, + currencyToSend = cryptoCurrencyConverter.convert(currencyToSend), + feeAmount = fee.feeValue, + gasLimit = fee.gasLimit, + destinationAddress = swapStateData.swapModel.transaction.toWalletAddress, + dataToSign = swapStateData.swapModel.transaction.data, + ), isSwap = true, derivationPath = derivationPath, + analyticsData = AnalyticsData( + feeType = fee.feeType.getNameForAnalytics(), + tokenSymbol = currencyToSend.symbol, + ), ) return when (result) { is SendTxResult.Success -> { @@ -587,12 +597,14 @@ internal class SwapInteractorImpl @Inject constructor( gasLimit = normalFeeGas, feeFiatFormatted = normalFiatFee, feeCryptoFormatted = normalCryptoFee, + feeType = FeeType.NORMAL, ), priorityFee = TxFee( feeValue = priorityFeeValue, gasLimit = priorityFeeGas, feeFiatFormatted = priorityFiatFee, feeCryptoFormatted = priorityCryptoFee, + feeType = FeeType.PRIORITY, ), ) } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapApproveType.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapApproveType.kt index 8690e83395..21ded70713 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapApproveType.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapApproveType.kt @@ -2,4 +2,11 @@ package com.tangem.feature.swap.domain.models.domain enum class SwapApproveType { LIMITED, UNLIMITED +} + +fun SwapApproveType.getNameForAnalytics(): String { + return when (this) { + SwapApproveType.LIMITED -> "Transaction" + SwapApproveType.UNLIMITED -> "Unlimited" + } } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt index 8e4258d548..d2b760740e 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt @@ -77,4 +77,16 @@ data class TxFee( val gasLimit: Int, val feeFiatFormatted: String, val feeCryptoFormatted: String, -) \ No newline at end of file + val feeType: FeeType, +) + +enum class FeeType { + NORMAL, PRIORITY +} + +fun FeeType.getNameForAnalytics(): String { + return when (this) { + FeeType.NORMAL -> "Normal" + FeeType.PRIORITY -> "Max" + } +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index b7a127af0b..ae0bfbff26 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -25,6 +25,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.getActiveIconRes import com.tangem.core.ui.extensions.getActiveIconResByCoinId import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.swap.domain.models.ui.FeeType import com.tangem.feature.swap.domain.models.ui.TxFee import com.tangem.feature.swap.models.* import com.tangem.feature.swap.presentation.R @@ -394,6 +395,7 @@ val stateSelectable = SelectableItemsState( gasLimit = 0, feeFiatFormatted = "", feeCryptoFormatted = "", + feeType = FeeType.NORMAL, ), ), items = listOf( @@ -407,6 +409,7 @@ val stateSelectable = SelectableItemsState( gasLimit = 0, feeFiatFormatted = "", feeCryptoFormatted = "", + feeType = FeeType.NORMAL, ), ), Item( @@ -419,6 +422,7 @@ val stateSelectable = SelectableItemsState( gasLimit = 0, feeFiatFormatted = "", feeCryptoFormatted = "", + feeType = FeeType.NORMAL, ), ), ).toImmutableList(), diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt index 8c7e7412f0..5a915b109e 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt @@ -1,8 +1,6 @@ package com.tangem.lib.crypto -import com.tangem.lib.crypto.models.Currency -import com.tangem.lib.crypto.models.ProxyFees -import com.tangem.lib.crypto.models.ProxyNetworkInfo +import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult import java.math.BigDecimal @@ -11,26 +9,25 @@ interface TransactionManager { @Suppress("LongParameterList") @Throws(IllegalStateException::class) suspend fun sendApproveTransaction( - networkId: String, - feeAmount: BigDecimal, - gasLimit: Int, - destinationAddress: String, - dataToSign: String, + txData: ApproveTxData, derivationPath: String?, + analyticsData: AnalyticsData, ): SendTxResult - @Suppress("LongParameterList") + /** + * Send transaction + * + * @param txData data to build a tx + * @param derivationPath for select right walletManager + * @param analyticsData data for send analytics event + * @return result of transaction + */ @Throws(IllegalStateException::class) suspend fun sendTransaction( - networkId: String, - amountToSend: BigDecimal, - feeAmount: BigDecimal, - gasLimit: Int, - destinationAddress: String, - dataToSign: String, + txData: SwapTxData, isSwap: Boolean, - currencyToSend: Currency, derivationPath: String?, + analyticsData: AnalyticsData, ): SendTxResult /** diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt new file mode 100644 index 0000000000..71a0845c8d --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt @@ -0,0 +1,14 @@ +package com.tangem.lib.crypto.models + +/** + * Analytics data for send events in analytics engine + * + * @property feeType type of fee (min,max,normal) + * @property tokenSymbol symbol + * @property permissionType optional parameter used for type tx approve + */ +data class AnalyticsData( + val feeType: String, + val tokenSymbol: String, + val permissionType: String? = null, +) \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt new file mode 100644 index 0000000000..f2bfab34e3 --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt @@ -0,0 +1,20 @@ +package com.tangem.lib.crypto.models + +import java.math.BigDecimal + +/** + * Tx data for create and make approve transaction + * + * @property networkId id of network + * @property feeAmount amount of fee + * @property gasLimit gasLimit for given tx + * @property destinationAddress address to send tx + * @property dataToSign data to sing with signer + */ +data class ApproveTxData( + val networkId: String, + val feeAmount: BigDecimal, + val gasLimit: Int, + val destinationAddress: String, + val dataToSign: String, +) \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt new file mode 100644 index 0000000000..723169c990 --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt @@ -0,0 +1,24 @@ +package com.tangem.lib.crypto.models + +import java.math.BigDecimal + +/** + * Tx data for create and make transaction + * + * @property networkId id of network + * @property feeAmount amount of fee + * @property gasLimit gasLimit for given tx + * @property destinationAddress address to send tx + * @property dataToSign data to sing with signer + * @property amountToSend amount of tx + * @property currencyToSend currency for tx + */ +data class SwapTxData( + val networkId: String, + val feeAmount: BigDecimal, + val gasLimit: Int, + val destinationAddress: String, + val dataToSign: String, + val amountToSend: BigDecimal, + val currencyToSend: Currency, +) \ No newline at end of file From b406e675f9897f6f40cb752bef31584c3a257b8a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 19:36:27 +0500 Subject: [PATCH 11/32] Updated on 2026-08-14 --- .../connection/RealInternetConnectionManager.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt index 669dc8ed20..935ec93a24 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt @@ -1,6 +1,7 @@ package com.tangem.datasource.connection import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler import io.reactivex.BackpressureStrategy import kotlinx.coroutines.CoroutineScope @@ -11,6 +12,8 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.reactive.asFlow +private const val PING_INTERVAL = 5_000 + internal class RealInternetConnectionManager : NetworkConnectionManager { private val scope = @@ -19,10 +22,14 @@ internal class RealInternetConnectionManager : NetworkConnectionManager { ) override val isOnlineFlow: StateFlow = ReactiveNetwork - .observeInternetConnectivity() + .observeInternetConnectivity( + InternetObservingSettings.builder() + .interval(PING_INTERVAL) + .build() + ) .toFlowable(BackpressureStrategy.LATEST) .asFlow() - .stateIn(scope, SharingStarted.Eagerly, initialValue = false) + .stateIn(scope, SharingStarted.Lazily, initialValue = false) override val isOnline: Boolean get() = isOnlineFlow.value From d2fbb3e96c2922c8ddb22503341ad67aa3e3672d Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 14:28:57 +0300 Subject: [PATCH 12/32] Updated on 2026-08-14 --- .../java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 25a3e74cf3..b9a301369f 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -682,7 +682,7 @@ internal class SwapInteractorImpl @Inject constructor( private const val DEFAULT_SLIPPAGE = 2 private const val ZERO_BALANCE = "0" private const val DEFAULT_BLOCKCHAIN_INCH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - private const val INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT = 1.4 + private const val INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT = 1.0 // if need to increase fee when check isEnough private const val INCREASE_GAS_LIMIT_BY = 125 // 25% private const val USDT_SYMBOL = "USDT" private const val USDC_SYMBOL = "USDC" From 428d28e0ffd8f1fcc4ad4db78ffddfbee4ed7469 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 18:28:35 +0300 Subject: [PATCH 13/32] Updated on 2026-08-14 --- .idea/codeStyles/Project.xml | 2 +- .../datasource/connection/RealInternetConnectionManager.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index ac3eeb6f5d..7fc608245b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -205,4 +205,4 @@ - \ No newline at end of file + diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt index 935ec93a24..2560245330 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt @@ -25,7 +25,7 @@ internal class RealInternetConnectionManager : NetworkConnectionManager { .observeInternetConnectivity( InternetObservingSettings.builder() .interval(PING_INTERVAL) - .build() + .build(), ) .toFlowable(BackpressureStrategy.LATEST) .asFlow() From 7b69b597b6e1bc32c43ca673bd65414fd37b8c6e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 30 May 2023 21:25:18 +0500 Subject: [PATCH 14/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 2d33326ff2..52c9d2906f 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,7 +73,7 @@ reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.6-227" +tangemBlockchainSdk = "release-app_4.6-229" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_4.6-251" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From e182d88ebe3a89d544a352c2ac10ef73625f40e9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 10:35:16 +0300 Subject: [PATCH 15/32] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 48 +++++++++++--------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7e632399d0..b9fd68f4ee 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,6 +49,7 @@ android:launchMode="singleTop" android:screenOrientation="portrait" android:windowSoftInputMode="adjustResize"> + @@ -58,40 +59,25 @@ - - - - - - + + + - - - - + + + - + + + - - - + + + + + + + From 73ba136063c0a999855065cf18344f126d11ef82 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 09:58:40 +0300 Subject: [PATCH 16/32] Updated on 2026-08-14 --- .../common/analytics/events/AnalyticsParam.kt | 50 ++++++++++++- .../tap/common/analytics/events/Basic.kt | 12 +++- .../send/redux/middlewares/SendMiddleware.kt | 72 ++++++++++--------- .../features/send/redux/states/FeeState.kt | 12 +++- .../tap/proxy/TransactionManagerImpl.kt | 70 +++++++++++------- .../feature/swap/domain/SwapInteractorImpl.kt | 46 +++++++----- .../domain/models/domain/SwapApproveType.kt | 7 ++ .../swap/domain/models/ui/SwapState.kt | 14 +++- .../feature/swap/ui/SwapScreenContent.kt | 4 ++ .../tangem/lib/crypto/TransactionManager.kt | 29 ++++---- .../tangem/lib/crypto/models/AnalyticsData.kt | 14 ++++ .../tangem/lib/crypto/models/ApproveTxData.kt | 20 ++++++ .../tangem/lib/crypto/models/SwapTxData.kt | 24 +++++++ 13 files changed, 274 insertions(+), 100 deletions(-) create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt index 15c29ad5d0..7a788fd5c6 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt @@ -87,12 +87,54 @@ sealed class AnalyticsParam { } sealed class TxSentFrom(val value: String) { - object Send : TxSentFrom("Send") - object Swap : TxSentFrom("Swap") + data class Send( + override val blockchain: String, + override val token: String, + override val feeType: FeeType, + ) : TxSentFrom("Send"), TxData + + data class Swap( + override val blockchain: String, + override val token: String, + override val feeType: FeeType, + ) : TxSentFrom("Swap"), TxData + + data class Approve( + override val blockchain: String, + override val token: String, + override val feeType: FeeType, + val permissionType: String, + ) : TxSentFrom("Approve"), TxData + object WalletConnect : TxSentFrom("WalletConnect") object Sell : TxSentFrom("Sell") } + sealed interface TxData { + val blockchain: String + val token: String + val feeType: FeeType + } + + sealed class FeeType(val value: String) { + object Fixed : FeeType("Fixed") + object Min : FeeType("Min") + object Normal : FeeType("Normal") + object Max : FeeType("Max") + + companion object { + fun fromString(feeType: String): FeeType { + return when (feeType) { + Min.value -> Min + Normal.value -> Normal + Max.value -> Max + Fixed.value -> Fixed + else -> Fixed + } + } + } + } + sealed class WalletCreationType(val value: String) { object PrivateKey : WalletCreationType("Private key") object NewSeed : WalletCreationType("New seed") @@ -100,9 +142,13 @@ sealed class AnalyticsParam { } companion object Key { + const val BLOCKCHAIN = "blockchain" + const val TOKEN = "Token" const val SOURCE = "Source" const val BALANCE = "Balance" const val BATCH = "Batch" + const val FEE_TYPE = "Fee Type" + const val PERMISSION_TYPE = "Permission Type" const val PRODUCT_TYPE = "Product Type" const val FIRMWARE = "Firmware" const val CURRENCY = "Currency" diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt index 4f63bd4961..d9b029fcae 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt @@ -51,7 +51,17 @@ sealed class Basic( class TransactionSent(sentFrom: AnalyticsParam.TxSentFrom) : Basic( event = "Transaction sent", - params = mapOf(AnalyticsParam.SOURCE to sentFrom.value), + params = buildMap { + this[AnalyticsParam.SOURCE] = sentFrom.value + if (sentFrom is AnalyticsParam.TxData) { + this[AnalyticsParam.BLOCKCHAIN] = sentFrom.blockchain + this[AnalyticsParam.TOKEN] = sentFrom.token + this[AnalyticsParam.FEE_TYPE] = sentFrom.feeType.value + } + if (sentFrom is AnalyticsParam.TxSentFrom.Approve) { + this[AnalyticsParam.PERMISSION_TYPE] = sentFrom.permissionType + } + }, ) class ScanError(error: Throwable) : Basic( diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 2d22340b26..dbc8be67c1 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -7,28 +7,20 @@ import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.ton.TonTransactionExtras import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.TransactionError -import com.tangem.blockchain.common.TransactionSender -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.* import com.tangem.blockchain.extensions.SimpleResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard import com.tangem.common.services.Result import com.tangem.core.analytics.Analytics -import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.extensions.withMainContext -import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE +import com.tangem.domain.models.scan.CardDTO +import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchErrorNotification -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.safeUpdate -import com.tangem.tap.common.extensions.stripZeroPlainString +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -39,25 +31,11 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.minimalAmount import com.tangem.tap.features.demo.DemoTransactionSender import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.send.redux.AddressPayIdActionUi -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction -import com.tangem.tap.features.send.redux.AmountAction -import com.tangem.tap.features.send.redux.AmountActionUi +import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee -import com.tangem.tap.features.send.redux.PrepareSendScreen -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.features.send.redux.SendActionUi -import com.tangem.tap.features.send.redux.states.ButtonState -import com.tangem.tap.features.send.redux.states.ExternalTransactionData -import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.TransactionExtrasState +import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.tangemSdk -import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletCurrenciesManager import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -65,7 +43,7 @@ import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware import timber.log.Timber -import java.util.* +import java.util.EnumSet /** [REDACTED_AUTHOR] @@ -134,9 +112,16 @@ private fun verifyAndSendTransaction( }, sendAllCallback = { sendTransaction( - action, walletManager, amountToSend, feeAmount, destinationAddress, - sendState.transactionExtrasState, card, sendState.externalTransactionData, - dispatch, + action = action, + walletManager = walletManager, + amountToSend = amountToSend, + feeAmount = feeAmount, + feeType = sendState.feeState.selectedFeeType, + destinationAddress = destinationAddress, + transactionExtras = sendState.transactionExtrasState, + card = card, + externalTransactionData = sendState.externalTransactionData, + dispatch = dispatch, ) }, reduceAmount, @@ -145,8 +130,16 @@ private fun verifyAndSendTransaction( } else -> { sendTransaction( - action, walletManager, amountToSend, feeAmount, destinationAddress, - sendState.transactionExtrasState, card, sendState.externalTransactionData, dispatch, + action = action, + walletManager = walletManager, + amountToSend = amountToSend, + feeAmount = feeAmount, + feeType = sendState.feeState.selectedFeeType, + destinationAddress = destinationAddress, + transactionExtras = sendState.transactionExtrasState, + card = card, + externalTransactionData = sendState.externalTransactionData, + dispatch = dispatch, ) } } @@ -158,6 +151,7 @@ private fun sendTransaction( walletManager: WalletManager, amountToSend: Amount, feeAmount: Amount, + feeType: FeeType, destinationAddress: String, transactionExtras: TransactionExtrasState, card: CardDTO, @@ -253,7 +247,15 @@ private fun sendTransaction( Analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Sell)) dispatch(WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)) } else { - Analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Send)) + Analytics.send( + Basic.TransactionSent( + sentFrom = AnalyticsParam.TxSentFrom.Send( + blockchain = walletManager.wallet.blockchain.fullName, + token = amountToSend.currencySymbol, + feeType = feeType.convertToAnalyticsFeeType(), + ), + ), + ) dispatch(NavigationAction.PopBackTo()) } scope.launch(Dispatchers.IO) { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt index e9c9472bb4..65067fc0f8 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.send.redux.states import com.tangem.blockchain.common.Amount +import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.features.wallet.redux.ProgressState import java.math.BigDecimal @@ -8,7 +9,16 @@ import java.math.BigDecimal [REDACTED_AUTHOR] */ enum class FeeType { - SINGLE, LOW, NORMAL, PRIORITY + SINGLE, LOW, NORMAL, PRIORITY; +} + +fun FeeType.convertToAnalyticsFeeType(): AnalyticsParam.FeeType { + return when (this) { + FeeType.SINGLE -> AnalyticsParam.FeeType.Fixed + FeeType.LOW -> AnalyticsParam.FeeType.Min + FeeType.NORMAL -> AnalyticsParam.FeeType.Normal + FeeType.PRIORITY -> AnalyticsParam.FeeType.Max + } } data class FeeState( diff --git a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt index dabd3c53ec..216203fce9 100644 --- a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -34,14 +34,11 @@ class TransactionManagerImpl( ) : TransactionManager { override suspend fun sendApproveTransaction( - networkId: String, - feeAmount: BigDecimal, - gasLimit: Int, - destinationAddress: String, - dataToSign: String, + txData: ApproveTxData, derivationPath: String?, + analyticsData: AnalyticsData, ): SendTxResult { - val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } + val blockchain = requireNotNull(Blockchain.fromNetworkId(txData.networkId)) { "blockchain not found" } val walletManager = getActualWalletManager(blockchain, derivationPath) walletManager.update() val amount = Amount(value = BigDecimal.ZERO, blockchain = blockchain) @@ -49,40 +46,55 @@ class TransactionManagerImpl( walletManager = walletManager, amount = amount, blockchain = blockchain, - feeAmount = feeAmount, - gasLimit = gasLimit, - destinationAddress = destinationAddress, - dataToSign = dataToSign, + feeAmount = txData.feeAmount, + gasLimit = txData.gasLimit, + destinationAddress = txData.destinationAddress, + dataToSign = txData.dataToSign, + successEvent = AnalyticsParam.TxSentFrom.Approve( + blockchain = blockchain.fullName, + token = analyticsData.tokenSymbol, + feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), + permissionType = analyticsData.permissionType ?: "", + ), ) } override suspend fun sendTransaction( - networkId: String, - amountToSend: BigDecimal, - feeAmount: BigDecimal, - gasLimit: Int, - destinationAddress: String, - dataToSign: String, + txData: SwapTxData, isSwap: Boolean, - currencyToSend: Currency, derivationPath: String?, + analyticsData: AnalyticsData, ): SendTxResult { - val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } + val blockchain = requireNotNull(Blockchain.fromNetworkId(txData.networkId)) { "blockchain not found" } val walletManager = getActualWalletManager(blockchain, derivationPath) walletManager.update() val amount = if (isSwap) { - createAmountForSwap(amountToSend, currencyToSend, blockchain) + createAmountForSwap(txData.amountToSend, txData.currencyToSend, blockchain) } else { - createAmount(amountToSend, currencyToSend, blockchain) + createAmount(txData.amountToSend, txData.currencyToSend, blockchain) + } + val successEvent = if (isSwap) { + AnalyticsParam.TxSentFrom.Swap( + blockchain = blockchain.fullName, + token = analyticsData.tokenSymbol, + feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), + ) + } else { + AnalyticsParam.TxSentFrom.Send( + blockchain = blockchain.fullName, + token = analyticsData.tokenSymbol, + feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), + ) } return sendTransactionInternal( walletManager = walletManager, amount = amount, blockchain = blockchain, - feeAmount = feeAmount, - gasLimit = gasLimit, - destinationAddress = destinationAddress, - dataToSign = dataToSign, + feeAmount = txData.feeAmount, + gasLimit = txData.gasLimit, + destinationAddress = txData.destinationAddress, + dataToSign = txData.dataToSign, + successEvent = successEvent, ) } @@ -95,6 +107,7 @@ class TransactionManagerImpl( gasLimit: Int, destinationAddress: String, dataToSign: String, + successEvent: AnalyticsParam.TxSentFrom, ): SendTxResult { val txData = walletManager.createTransaction( amount = amount, @@ -110,7 +123,10 @@ class TransactionManagerImpl( FirebaseCrashlytics.getInstance().recordException(ex) return SendTxResult.UnknownError(ex) } - return handleSendResult(sendResult) + return handleSendResult( + result = sendResult, + successEvent = successEvent, + ) } override fun getExplorerTransactionLink(networkId: String, txAddress: String): String { @@ -319,10 +335,10 @@ class TransactionManagerImpl( } } - private fun handleSendResult(result: SimpleResult): SendTxResult { + private fun handleSendResult(result: SimpleResult, successEvent: AnalyticsParam.TxSentFrom): SendTxResult { when (result) { is SimpleResult.Success -> { - analytics.send(Basic.TransactionSent(AnalyticsParam.TxSentFrom.Swap)) + analytics.send(Basic.TransactionSent(sentFrom = successEvent)) return SendTxResult.Success } is SimpleResult.Failure -> { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 835ebdf273..25a3e74cf3 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -4,16 +4,13 @@ import com.tangem.feature.swap.domain.cache.SwapDataCache import com.tangem.feature.swap.domain.converters.CryptoCurrencyConverter import com.tangem.feature.swap.domain.models.DataError import com.tangem.feature.swap.domain.models.SwapAmount +import com.tangem.feature.swap.domain.models.domain.* import com.tangem.feature.swap.domain.models.domain.Currency -import com.tangem.feature.swap.domain.models.domain.PermissionOptions -import com.tangem.feature.swap.domain.models.domain.PreparedSwapConfigState -import com.tangem.feature.swap.domain.models.domain.SwapApproveType import com.tangem.feature.swap.domain.models.toStringWithRightOffset import com.tangem.feature.swap.domain.models.ui.* import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager -import com.tangem.lib.crypto.models.ProxyFees -import com.tangem.lib.crypto.models.ProxyFiatCurrency +import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult import com.tangem.utils.toFiatString import java.math.BigDecimal @@ -115,12 +112,19 @@ internal class SwapInteractorImpl @Inject constructor( permissionOptions.approveData.approveModel.data } val result = transactionManager.sendApproveTransaction( - networkId = networkId, - feeAmount = permissionOptions.txFee.feeValue, - gasLimit = permissionOptions.txFee.gasLimit, - destinationAddress = permissionOptions.approveData.approveModel.toAddress, - dataToSign = dataToSign, + txData = ApproveTxData( + networkId = networkId, + feeAmount = permissionOptions.txFee.feeValue, + gasLimit = permissionOptions.txFee.gasLimit, + destinationAddress = permissionOptions.approveData.approveModel.toAddress, + dataToSign = dataToSign, + ), derivationPath = derivationPath, + analyticsData = AnalyticsData( + feeType = permissionOptions.txFee.feeType.getNameForAnalytics(), + tokenSymbol = permissionOptions.fromToken.symbol, + permissionType = permissionOptions.approveType.getNameForAnalytics(), + ), ) return when (result) { is SendTxResult.Success -> { @@ -188,15 +192,21 @@ internal class SwapInteractorImpl @Inject constructor( ): TxState { val amount = requireNotNull(toBigDecimalOrNull(amountToSwap)) { "wrong amount format, use only digits" } val result = transactionManager.sendTransaction( - networkId = networkId, - amountToSend = amount, - currencyToSend = cryptoCurrencyConverter.convert(currencyToSend), - feeAmount = fee.feeValue, - gasLimit = fee.gasLimit, - destinationAddress = swapStateData.swapModel.transaction.toWalletAddress, - dataToSign = swapStateData.swapModel.transaction.data, + txData = SwapTxData( + networkId = networkId, + amountToSend = amount, + currencyToSend = cryptoCurrencyConverter.convert(currencyToSend), + feeAmount = fee.feeValue, + gasLimit = fee.gasLimit, + destinationAddress = swapStateData.swapModel.transaction.toWalletAddress, + dataToSign = swapStateData.swapModel.transaction.data, + ), isSwap = true, derivationPath = derivationPath, + analyticsData = AnalyticsData( + feeType = fee.feeType.getNameForAnalytics(), + tokenSymbol = currencyToSend.symbol, + ), ) return when (result) { is SendTxResult.Success -> { @@ -587,12 +597,14 @@ internal class SwapInteractorImpl @Inject constructor( gasLimit = normalFeeGas, feeFiatFormatted = normalFiatFee, feeCryptoFormatted = normalCryptoFee, + feeType = FeeType.NORMAL, ), priorityFee = TxFee( feeValue = priorityFeeValue, gasLimit = priorityFeeGas, feeFiatFormatted = priorityFiatFee, feeCryptoFormatted = priorityCryptoFee, + feeType = FeeType.PRIORITY, ), ) } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapApproveType.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapApproveType.kt index 8690e83395..21ded70713 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapApproveType.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/SwapApproveType.kt @@ -2,4 +2,11 @@ package com.tangem.feature.swap.domain.models.domain enum class SwapApproveType { LIMITED, UNLIMITED +} + +fun SwapApproveType.getNameForAnalytics(): String { + return when (this) { + SwapApproveType.LIMITED -> "Transaction" + SwapApproveType.UNLIMITED -> "Unlimited" + } } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt index 8e4258d548..d2b760740e 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt @@ -77,4 +77,16 @@ data class TxFee( val gasLimit: Int, val feeFiatFormatted: String, val feeCryptoFormatted: String, -) \ No newline at end of file + val feeType: FeeType, +) + +enum class FeeType { + NORMAL, PRIORITY +} + +fun FeeType.getNameForAnalytics(): String { + return when (this) { + FeeType.NORMAL -> "Normal" + FeeType.PRIORITY -> "Max" + } +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index b7a127af0b..ae0bfbff26 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -25,6 +25,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.getActiveIconRes import com.tangem.core.ui.extensions.getActiveIconResByCoinId import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.swap.domain.models.ui.FeeType import com.tangem.feature.swap.domain.models.ui.TxFee import com.tangem.feature.swap.models.* import com.tangem.feature.swap.presentation.R @@ -394,6 +395,7 @@ val stateSelectable = SelectableItemsState( gasLimit = 0, feeFiatFormatted = "", feeCryptoFormatted = "", + feeType = FeeType.NORMAL, ), ), items = listOf( @@ -407,6 +409,7 @@ val stateSelectable = SelectableItemsState( gasLimit = 0, feeFiatFormatted = "", feeCryptoFormatted = "", + feeType = FeeType.NORMAL, ), ), Item( @@ -419,6 +422,7 @@ val stateSelectable = SelectableItemsState( gasLimit = 0, feeFiatFormatted = "", feeCryptoFormatted = "", + feeType = FeeType.NORMAL, ), ), ).toImmutableList(), diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt index 8c7e7412f0..5a915b109e 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/TransactionManager.kt @@ -1,8 +1,6 @@ package com.tangem.lib.crypto -import com.tangem.lib.crypto.models.Currency -import com.tangem.lib.crypto.models.ProxyFees -import com.tangem.lib.crypto.models.ProxyNetworkInfo +import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult import java.math.BigDecimal @@ -11,26 +9,25 @@ interface TransactionManager { @Suppress("LongParameterList") @Throws(IllegalStateException::class) suspend fun sendApproveTransaction( - networkId: String, - feeAmount: BigDecimal, - gasLimit: Int, - destinationAddress: String, - dataToSign: String, + txData: ApproveTxData, derivationPath: String?, + analyticsData: AnalyticsData, ): SendTxResult - @Suppress("LongParameterList") + /** + * Send transaction + * + * @param txData data to build a tx + * @param derivationPath for select right walletManager + * @param analyticsData data for send analytics event + * @return result of transaction + */ @Throws(IllegalStateException::class) suspend fun sendTransaction( - networkId: String, - amountToSend: BigDecimal, - feeAmount: BigDecimal, - gasLimit: Int, - destinationAddress: String, - dataToSign: String, + txData: SwapTxData, isSwap: Boolean, - currencyToSend: Currency, derivationPath: String?, + analyticsData: AnalyticsData, ): SendTxResult /** diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt new file mode 100644 index 0000000000..71a0845c8d --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt @@ -0,0 +1,14 @@ +package com.tangem.lib.crypto.models + +/** + * Analytics data for send events in analytics engine + * + * @property feeType type of fee (min,max,normal) + * @property tokenSymbol symbol + * @property permissionType optional parameter used for type tx approve + */ +data class AnalyticsData( + val feeType: String, + val tokenSymbol: String, + val permissionType: String? = null, +) \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt new file mode 100644 index 0000000000..f2bfab34e3 --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt @@ -0,0 +1,20 @@ +package com.tangem.lib.crypto.models + +import java.math.BigDecimal + +/** + * Tx data for create and make approve transaction + * + * @property networkId id of network + * @property feeAmount amount of fee + * @property gasLimit gasLimit for given tx + * @property destinationAddress address to send tx + * @property dataToSign data to sing with signer + */ +data class ApproveTxData( + val networkId: String, + val feeAmount: BigDecimal, + val gasLimit: Int, + val destinationAddress: String, + val dataToSign: String, +) \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt new file mode 100644 index 0000000000..723169c990 --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt @@ -0,0 +1,24 @@ +package com.tangem.lib.crypto.models + +import java.math.BigDecimal + +/** + * Tx data for create and make transaction + * + * @property networkId id of network + * @property feeAmount amount of fee + * @property gasLimit gasLimit for given tx + * @property destinationAddress address to send tx + * @property dataToSign data to sing with signer + * @property amountToSend amount of tx + * @property currencyToSend currency for tx + */ +data class SwapTxData( + val networkId: String, + val feeAmount: BigDecimal, + val gasLimit: Int, + val destinationAddress: String, + val dataToSign: String, + val amountToSend: BigDecimal, + val currencyToSend: Currency, +) \ No newline at end of file From 83333659d510a58d63a6b5812f691fee592e914a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 19:36:27 +0500 Subject: [PATCH 17/32] Updated on 2026-08-14 --- .../connection/RealInternetConnectionManager.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt index 669dc8ed20..935ec93a24 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt @@ -1,6 +1,7 @@ package com.tangem.datasource.connection import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork +import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler import io.reactivex.BackpressureStrategy import kotlinx.coroutines.CoroutineScope @@ -11,6 +12,8 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.reactive.asFlow +private const val PING_INTERVAL = 5_000 + internal class RealInternetConnectionManager : NetworkConnectionManager { private val scope = @@ -19,10 +22,14 @@ internal class RealInternetConnectionManager : NetworkConnectionManager { ) override val isOnlineFlow: StateFlow = ReactiveNetwork - .observeInternetConnectivity() + .observeInternetConnectivity( + InternetObservingSettings.builder() + .interval(PING_INTERVAL) + .build() + ) .toFlowable(BackpressureStrategy.LATEST) .asFlow() - .stateIn(scope, SharingStarted.Eagerly, initialValue = false) + .stateIn(scope, SharingStarted.Lazily, initialValue = false) override val isOnline: Boolean get() = isOnlineFlow.value From 9865c83f37c0338e61b0d7d67906200bd83327af Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 14:28:57 +0300 Subject: [PATCH 18/32] Updated on 2026-08-14 --- .../java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 25a3e74cf3..b9a301369f 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -682,7 +682,7 @@ internal class SwapInteractorImpl @Inject constructor( private const val DEFAULT_SLIPPAGE = 2 private const val ZERO_BALANCE = "0" private const val DEFAULT_BLOCKCHAIN_INCH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - private const val INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT = 1.4 + private const val INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT = 1.0 // if need to increase fee when check isEnough private const val INCREASE_GAS_LIMIT_BY = 125 // 25% private const val USDT_SYMBOL = "USDT" private const val USDC_SYMBOL = "USDC" From e4502114a318d6bf1629c2c5b67f85be46771f5d Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 31 May 2023 18:28:35 +0300 Subject: [PATCH 19/32] Updated on 2026-08-14 --- .idea/codeStyles/Project.xml | 2 +- .../datasource/connection/RealInternetConnectionManager.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index ac3eeb6f5d..7fc608245b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -205,4 +205,4 @@ - \ No newline at end of file + diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt index 935ec93a24..2560245330 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt @@ -25,7 +25,7 @@ internal class RealInternetConnectionManager : NetworkConnectionManager { .observeInternetConnectivity( InternetObservingSettings.builder() .interval(PING_INTERVAL) - .build() + .build(), ) .toFlowable(BackpressureStrategy.LATEST) .asFlow() From b7384d89ea08c224a918c4b2fe5e37ed723ab70b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Jun 2023 16:40:54 +0300 Subject: [PATCH 20/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 52c9d2906f..13c4774474 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -75,7 +75,7 @@ reactiveNetwork = "3.0.8" # region Tangem tangemBlockchainSdk = "release-app_4.6-229" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "release-app_4.6-251" +tangemCardSdk = "release-app_4.6-254" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem From 80ceda2fd1523224953a7090523667389a06e8b8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 1 Jun 2023 16:33:27 +0300 Subject: [PATCH 21/32] Updated on 2026-08-14 --- .../impl/presentation/ui/BriefNetworksList.kt | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/BriefNetworksList.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/BriefNetworksList.kt index 9ecb139cae..104ebf9d8c 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/BriefNetworksList.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/BriefNetworksList.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.key import androidx.compose.ui.Alignment @@ -21,6 +22,9 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.tap.features.tokens.impl.presentation.states.NetworkItemState import kotlinx.collections.immutable.ImmutableCollection +/** This const configures how many items show to user and hide more than*/ +private const val MAX_VISIBLE_BRIEF_ICONS = 9 + /** [REDACTED_AUTHOR] */ @@ -37,10 +41,25 @@ internal fun BriefNetworksList( exit = fadeOut(), ) { Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) { - networks.forEach { network -> - key(network.name + network.protocolName) { - BriefNetworkItem(model = network) + val iterator = networks.iterator() + var index = 0 + while (iterator.hasNext()) { + val network = iterator.next() + if (index < MAX_VISIBLE_BRIEF_ICONS) { + key(network.name + network.protocolName) { + BriefNetworkItem(model = network) + } + } else { + if (networks.size < MAX_VISIBLE_BRIEF_ICONS + 1) { + key(network.name + network.protocolName) { + BriefNetworkItem(model = network) + } + } else { + HasMoreItem(moreCount = networks.size - index) + break + } } + index++ } } } @@ -50,8 +69,8 @@ internal fun BriefNetworksList( [REDACTED_AUTHOR] */ @Composable -internal fun BriefNetworkItem(model: NetworkItemState) { - Box(modifier = Modifier.size(size = TangemTheme.dimens.size20)) { +internal fun BriefNetworkItem(model: NetworkItemState, modifier: Modifier = Modifier) { + Box(modifier = modifier.size(size = TangemTheme.dimens.size20)) { Image( painter = painterResource(id = model.iconResId.value), contentDescription = null, @@ -76,4 +95,21 @@ internal fun BriefNetworkItem(model: NetworkItemState) { } } } +} + +@Composable +internal fun HasMoreItem(moreCount: Int) { + Box( + modifier = Modifier + .size(size = TangemTheme.dimens.size20) + .clip(CircleShape) + .background(TangemTheme.colors.control.unchecked), + ) { + Text( + modifier = Modifier.align(Alignment.Center), + text = "+$moreCount", + style = TangemTheme.typography.overline, + color = TangemTheme.colors.text.tertiary, + ) + } } \ No newline at end of file From e941448f3a115676150035d92f85229747f1dc84 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 2 Jun 2023 17:59:40 +0500 Subject: [PATCH 22/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 13c4774474..8534bab9fa 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,7 +73,7 @@ reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.6-229" +tangemBlockchainSdk = "release-app_4.6-230" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_4.6-254" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From c2e95c92ae66dfafa0ab0a4fca54951118cf9e45 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 5 Jun 2023 19:37:50 +0800 Subject: [PATCH 23/32] Updated on 2026-08-14 --- .../welcome/redux/WelcomeMiddleware.kt | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 6bd65c13eb..36f85b0311 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -1,13 +1,10 @@ package com.tangem.tap.features.welcome.redux import android.content.Intent +import com.tangem.common.* import com.tangem.common.core.TangemSdkError -import com.tangem.common.doOnFailure -import com.tangem.common.doOnResult -import com.tangem.common.doOnSuccess -import com.tangem.common.flatMap -import com.tangem.common.map import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.extensions.dispatchOnMain @@ -21,12 +18,6 @@ import com.tangem.tap.domain.scanCard.ScanCardProcessor import com.tangem.tap.domain.userWalletList.unlockIfLockable import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError import com.tangem.tap.features.signin.redux.SignInAction -import com.tangem.tap.intentHandler -import com.tangem.tap.preferencesStorage -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userWalletsListManager import kotlinx.coroutines.launch import org.rekotlin.Middleware import timber.log.Timber @@ -121,9 +112,10 @@ internal class WelcomeMiddleware { } private fun handleInitialIntent(intent: Intent?) { - val isBackgroundScanWasHandled = intentHandler.handleBackgroundScan(intent, hasSavedUserWallets = true) + val isBackgroundScanNotHandled = !intentHandler.handleBackgroundScan(intent, hasSavedUserWallets = true) + val hasNotIncompletedBackup = !backupService.hasIncompletedBackup - if (!isBackgroundScanWasHandled) { + if (isBackgroundScanNotHandled && hasNotIncompletedBackup) { store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics) } } From aa2b4bec5a808e1d252f3b17c9b1da8ddae60b10 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 5 Jun 2023 17:07:36 +0500 Subject: [PATCH 24/32] Updated on 2026-08-14 --- core/datasource/build.gradle.kts | 2 +- .../connection/RealInternetConnectionManager.kt | 11 ++++++++++- gradle/dependencies.toml | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 3350c7930f..de4006b63a 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -22,7 +22,7 @@ dependencies { /** Coroutines */ implementation(deps.kotlin.coroutines) - implementation(deps.kotlin.coroutines.reactive) + implementation(deps.kotlin.coroutines.rx2) /** Logging */ implementation(deps.timber) diff --git a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt index 2560245330..8ca86a2587 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/connection/RealInternetConnectionManager.kt @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler import io.reactivex.BackpressureStrategy +import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -21,6 +22,14 @@ internal class RealInternetConnectionManager : NetworkConnectionManager { SupervisorJob() + Dispatchers.IO + FeatureCoroutineExceptionHandler.create("RealInternetConnectionManager"), ) + private val initialNetworkResult: Boolean by lazy { + ReactiveNetwork + .checkInternetConnectivity() + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .blockingGet() + } + override val isOnlineFlow: StateFlow = ReactiveNetwork .observeInternetConnectivity( InternetObservingSettings.builder() @@ -29,7 +38,7 @@ internal class RealInternetConnectionManager : NetworkConnectionManager { ) .toFlowable(BackpressureStrategy.LATEST) .asFlow() - .stateIn(scope, SharingStarted.Lazily, initialValue = false) + .stateIn(scope, SharingStarted.Lazily, initialValue = initialNetworkResult) override val isOnline: Boolean get() = isOnlineFlow.value diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 8534bab9fa..d82a5ce419 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -175,7 +175,7 @@ appsflyer = { module = "com.appsflyer:af-android-sdk", version.ref = "appsflyer" armadillo = { module = "at.favre.lib:armadillo", version.ref = "armadillo" } coil = { module = "io.coil-kt:coil", version.ref = "coil" } kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutine" } -kotlin-coroutines-reactive = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactive", version.ref = "coroutine" } +kotlin-coroutines-rx2 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2", version.ref = "coroutine" } kotlin-immutable-collections = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "kotlin-immutable-collections" } desugar = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugarJdkLibs" } googlePlay-core = { module = "com.google.android.play:core", version.ref = "googlePlayCore" } From 77170e7036e25a1875328eae41e640640046e76d Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Jun 2023 18:44:07 +0800 Subject: [PATCH 25/32] Updated on 2026-08-14 --- .../java/com/tangem/tap/common/analytics/events/Token.kt | 6 ------ .../tap/features/send/redux/middlewares/SendMiddleware.kt | 5 ----- .../com/tangem/feature/swap/domain/SwapInteractorImpl.kt | 2 +- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt index 46e02683ae..85dbfc7936 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt @@ -77,12 +77,6 @@ sealed class Token( class ButtonPaste : Send("Button - Paste") class ButtonQRCode : Send("Button - QR Code") class ButtonSwapCurrency : Send("Button - Swap Currency") - - class TransactionSent(type: CurrencyType, error: Throwable? = null) : Send( - event = "Transaction Sent", - params = mapOf("Token" to type.value), - error = error, - ) } sealed class Topup( diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index dbc8be67c1..c8938497e1 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -19,7 +19,6 @@ import com.tangem.domain.models.scan.CardDTO import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.events.Token import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState @@ -237,10 +236,8 @@ private fun sendTransaction( dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED)) tangemSdk.config.linkedTerminal = linkedTerminalState - val currencyType = AnalyticsParam.CurrencyType.Amount(amountToSend) when (sendResult) { is SimpleResult.Success -> { - Analytics.send(Token.Send.TransactionSent(currencyType)) dispatch(SendAction.SendSuccess) if (externalTransactionData != null) { @@ -273,8 +270,6 @@ private fun sendTransaction( ) val error = sendResult.error as? BlockchainSdkError ?: return@withMainContext - Analytics.send(Token.Send.TransactionSent(currencyType, error)) - when (error) { is BlockchainSdkError.WrappedTangemError -> { val tangemSdkError = error.tangemError as? TangemSdkError ?: return@withMainContext diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index b9a301369f..f3f193b180 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -683,7 +683,7 @@ internal class SwapInteractorImpl @Inject constructor( private const val ZERO_BALANCE = "0" private const val DEFAULT_BLOCKCHAIN_INCH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" private const val INCREASE_FEE_TO_CHECK_ENOUGH_PERCENT = 1.0 // if need to increase fee when check isEnough - private const val INCREASE_GAS_LIMIT_BY = 125 // 25% + private const val INCREASE_GAS_LIMIT_BY = 112 // 12% private const val USDT_SYMBOL = "USDT" private const val USDC_SYMBOL = "USDC" private const val INFINITY_SYMBOL = "∞" From d8aaf468c7c84bb6ce193d874f8b7bc4cdd39e97 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Jun 2023 23:41:36 +0800 Subject: [PATCH 26/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index d82a5ce419..cc00dada47 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,7 +73,7 @@ reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.6-230" +tangemBlockchainSdk = "release-app_4.6-232" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_4.6-254" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From 9b805a4071389f8de2cf21e6139ecdf6698f8a93 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Jun 2023 17:55:30 +0800 Subject: [PATCH 27/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index cc00dada47..deb623a8eb 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,7 +73,7 @@ reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.6-232" +tangemBlockchainSdk = "release-app_4.6-233" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_4.6-254" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From 18928fc683250b988a29b38dfd4db281626c25cc Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Jun 2023 18:24:54 +0800 Subject: [PATCH 28/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index deb623a8eb..4ed11fbe96 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,7 +73,7 @@ reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.6-233" +tangemBlockchainSdk = "release-app_4.6-235" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_4.6-254" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From 701c8d2a7f27261d097552fa999cf6b12bc2a55d Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Jun 2023 23:27:44 +0800 Subject: [PATCH 29/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 4ed11fbe96..a25d43fef1 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -73,7 +73,7 @@ reactiveNetwork = "3.0.8" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.6-235" +tangemBlockchainSdk = "release-app_4.6-239" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_4.6-254" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From eb67f369077fba99e1df09f63ff2821a3ee8f7cb Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 10 Jun 2023 14:01:40 +0800 Subject: [PATCH 30/32] Updated on 2026-08-14 --- .../disclaimer/ui/DisclaimerFragment.kt | 14 ++-- .../disclaimer/ui/LocalTermOfServices.kt | 66 +++++++++++++++++++ .../tap/features/shop/ui/ShopFragment.kt | 58 +--------------- core/res/src/main/res/values-ru/strings.xml | 1 - core/res/src/main/res/values/strings.xml | 3 +- 5 files changed, 78 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/disclaimer/ui/LocalTermOfServices.kt diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt index 62a0161bb8..8856bd5040 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.disclaimer.ui import android.os.Bundle import android.view.View import android.view.View.OVER_SCROLL_NEVER +import android.webkit.WebView import androidx.transition.TransitionInflater import by.kirich1409.viewbindingdelegate.viewBinding import com.tangem.core.ui.fragments.setStatusBarColor @@ -111,16 +112,17 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs } ProgressState.Error -> { root.beginDelayedTransition() - webView.hide() - groupAccept.hide() + webView.show() + groupAccept.show() groupLoading.hide() - groupError.show() + groupError.hide() + webView.loadLocalTermsOfServices() } - else -> {} + else -> Unit } } - override fun handleOnBackPressed() { - store.dispatch(DisclaimerAction.OnBackPressed) + private fun WebView.loadLocalTermsOfServices() { + loadData(localTermsOfServices, "text/html", "UTF-8") } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/LocalTermOfServices.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/LocalTermOfServices.kt new file mode 100644 index 0000000000..6651d0fa75 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/LocalTermOfServices.kt @@ -0,0 +1,66 @@ +package com.tangem.tap.features.disclaimer.ui + +internal val localTermsOfServices = """ + + + + + + + Legal Disclaimer + + +

Legal Disclaimer

+

    1. Tangem application (Software)

+

    The Software is intended for usage only with Tangem hardware wallets (Cards) via NFC interface. The + Software DOES NOT:

+

    a) Generate, store, transmit, or have access to private (secret) cryptographic keys to blockchain + wallets holding digital assets, including crypto-currency.

+

    b) Generate, store, transmit, or have access to secret keys, passwords, passphrases, recovery phrases + that can be used to restore or to copy private (secret) keys to blockchain wallets holding digital assets, including + crypto-currency.

+

    c) Provide exchange, trading, investment services on behalf of Tangem AG.

+

    2. Risks related to the use of Software

+

    Tangem will not be responsible for any losses, damages or claims arising from events falling within the + scope of the following five categories:

+

    a) Mistakes made by the user of any cryptocurrency-related software or service, e.g., forgotten + passwords, payments sent to wrong addresses, and accidental deletion of blockchain wallets on Cards.

+

    b) Problems of Software and/or any blockchain- or cryptocurrency- related software or service, e.g., + corrupted files, incorrectly constructed transactions, unsafe cryptographic libraries, malware.

+

    c) Technical failures in the hardware of the user, including Cards, of any cryptocurrency-related + software or service, e.g., data loss due to a faulty or damaged storage device.

+

    d) Security problems experienced by the user of any cryptocurrency-related software or service, e.g., + unauthorized access to users' wallets and/or accounts.

+

    e) Actions or inactions of third parties and/or events experienced by third parties, e.g., bankruptcy + of service providers, information security attacks on service providers, and fraud conducted by third parties.

+

    3. Trading and Investment risks

+

    There is considerable exposure to risk in any crypto-currency or other digital asset exchange + transaction. Any transaction involving currencies involves risks including, but not limited to, the potential for + changing economic conditions that may substantially affect the price or liquidity of a currency. Investments in + crypto-currency exchange speculation may also be susceptible to sharp rises and falls as the relevant market values + fluctuate. It is for this reason that when speculating in such markets it is advisable to use only risk capital.

+

    4. Electronic Trading Risks

+

    Before you engage in transactions using an electronic system, you should carefully review the rules and + regulations of the exchanges offering the system and/or listing the instruments you intend to trade. Online trading + has inherent risk due to system response and access times that may vary due to market conditions, system + performance, and other factors. You should understand these and additional risks before trading.

+

    5. Compliance with tax obligations        

+

    The users of the Software are solely responsible to determinate what, if any, taxes apply to their + crypto-currency transactions. The owners of, or contributors to, the Software are NOT responsible for determining + the taxes that apply to crypto-currency transactions.        

+

    6. No warranties        

+

    The Software is provided on an "as is" basis without any warranties of any kind regarding the + Software and/or any content, data, materials and/or services provided on the Software.       +  

+

    7. Limitation of liability

+

    Unless otherwise required by law, in no event shall the owners of, or contributors to, the Software be + liable for any damages of any kind, including, but not limited to, loss of use, loss of profits, or loss of data + arising out of or in any way connected with the use of the Software. In no way are the owners of, or contributors + to, the Software responsible for the actions, decisions, or other behavior taken or not taken by you in reliance + upon the Software.

+

    8. Last amendment

+

    This disclaimer was amended for the last time on October 1st, 2020.

+ +""".trimIndent() \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt index ad760cf53b..dccbe4d7b7 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt @@ -4,9 +4,6 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.content.Context import android.os.Bundle -import android.text.Spannable -import android.text.SpannableString -import android.text.style.ForegroundColorSpan import android.view.View import android.view.View.OnFocusChangeListener import android.view.inputmethod.EditorInfo @@ -15,9 +12,7 @@ import androidx.activity.OnBackPressedCallback import by.kirich1409.viewbindingdelegate.viewBinding import com.tangem.tap.common.GlobalLayoutStateHandler import com.tangem.tap.common.KeyboardObserver -import com.tangem.tap.common.extensions.getColorCompat import com.tangem.tap.common.extensions.getQuantityString -import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.shop.data.ProductType @@ -28,27 +23,14 @@ import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.FragmentShopBinding import org.rekotlin.StoreSubscriber -import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Locale class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber { - private val dateFormat = SimpleDateFormat("dd MMMM", Locale.getDefault()) private val binding: FragmentShopBinding by viewBinding(FragmentShopBinding::bind) - private val endPreorderTime = Calendar.getInstance() - private val isBuyAvailable: Boolean - private var cardTranslationY = 70f private lateinit var keyboardObserver: KeyboardObserver - init { - endPreorderTime.clear() - endPreorderTime.set(END_DATE_SOLD_OUT_YEAR, END_DATE_SOLD_OUT_MONTH, END_DATE_SOLD_OUT_DAY) - isBuyAvailable = System.currentTimeMillis() > endPreorderTime.timeInMillis - } - override fun subscribeToStore() { store.subscribe(this) { state -> state.skipRepeats { oldState, newState -> @@ -83,7 +65,6 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber< setupCardsImages() setupProductSelection() setupPromoCodeEditText() - setupSoldOutDescription() binding.toolbar.setNavigationOnClickListener { requireActivity().onBackPressed() @@ -146,26 +127,6 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber< } } - private fun setupSoldOutDescription() = with(binding) { - context?.let { cntx -> - if (!isBuyAvailable) { - val date = "${dateFormat.format(endPreorderTime.time)}!" - val soldOutTitle = cntx.getString(R.string.shop_sold_out_description_prefix) - val generalString = "$soldOutTitle $date" - val wordToSpan: Spannable = SpannableString(generalString) - wordToSpan.setSpan( - ForegroundColorSpan(cntx.getColorCompat(R.color.textBlack)), - generalString.length - date.length, - generalString.length, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, - ) - tvSoldOutDesc.text = wordToSpan - } else { - tvSoldOutDesc.hide() - } - } - } - override fun newState(state: ShopState) { if (activity == null || view == null) return @@ -213,16 +174,9 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber< } private fun handleButtonsState(state: ShopState) = with(binding) { - if (isBuyAvailable) { - btnPayGooglePay.root.show(state.isGooglePayAvailable) - btnAlternativePayment.show(state.isGooglePayAvailable) - btnMainAction.show(!state.isGooglePayAvailable) - } else { - btnPayGooglePay.root.hide() - btnAlternativePayment.hide() - btnMainAction.show() - btnMainAction.text = context?.getString(R.string.shop_pre_order_now) - } + btnPayGooglePay.root.show(state.isGooglePayAvailable) + btnAlternativePayment.show(state.isGooglePayAvailable) + btnMainAction.show(!state.isGooglePayAvailable) if (state.total != null) { btnAlternativePayment.setOnClickListener { store.dispatch(ShopAction.StartWebCheckout) } @@ -235,10 +189,4 @@ class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSubscriber< store.dispatch(ShopAction.ResetState) super.handleOnBackPressed() } - - companion object { - private const val END_DATE_SOLD_OUT_YEAR = 2023 - private const val END_DATE_SOLD_OUT_MONTH = 5 // June, month start counting from zero! - private const val END_DATE_SOLD_OUT_DAY = 10 - } } \ No newline at end of file diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 848323812c..4d108a3d06 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -334,7 +334,6 @@ У меня есть промо-код… Tangem Wallet Другие способы оплаты - Сделать предзаказ Все распродано, но не огорчайтесь! Мы уже производим новые партии Tangem Wallet. Чтобы получить ваш кошелек скорее, оформите предзаказ сейчас. Отправка заказов начнется Итого Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 76dbb81143..00da984b81 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -330,8 +330,7 @@ I have a promo code… Tangem Wallet Other payment methods - Pre-order now - Sold out, but don\'t worry! We are working very hard to get more for you! Make sure to place a pre-order here. Deliveries start on + Due to the high volume of orders we are receiving, Shipping and Local Delivery orders may be delayed. Total Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free. Store your crypto assets secure while keeping private keys contained in your card From a9b25a6127981d87f2736ee334d1f796b3d07155 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 10 Jun 2023 21:18:21 +0800 Subject: [PATCH 31/32] Updated on 2026-08-14 --- .../disclaimer/ui/DisclaimerFragment.kt | 39 +++++++++++-------- core/res/src/main/res/values-ru/strings.xml | 2 +- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt index 8856bd5040..c54f4ff005 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt @@ -28,21 +28,6 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs private val binding: FragmentDisclaimerBinding by viewBinding(FragmentDisclaimerBinding::bind) private val webViewClient = DisclaimerWebViewClient() - override fun configureTransitions() { - val inflater = TransitionInflater.from(requireContext()) - when (store.state.disclaimerState.showedFromScreen) { - AppScreen.Home -> { - enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom) - exitTransition = inflater.inflateTransition(android.R.transition.slide_top) - } - AppScreen.Details -> { - enterTransition = inflater.inflateTransition(android.R.transition.fade) - exitTransition = inflater.inflateTransition(android.R.transition.fade) - } - else -> {} - } - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) addBackPressHandler(handler = this) @@ -87,11 +72,29 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs super.onStop() } + override fun configureTransitions() { + val inflater = TransitionInflater.from(requireContext()) + when (store.state.disclaimerState.showedFromScreen) { + AppScreen.Home -> { + enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom) + exitTransition = inflater.inflateTransition(android.R.transition.slide_top) + } + AppScreen.Details -> { + enterTransition = inflater.inflateTransition(android.R.transition.fade) + exitTransition = inflater.inflateTransition(android.R.transition.fade) + } + else -> {} + } + } + + override fun handleOnBackPressed() { + store.dispatch(DisclaimerAction.OnBackPressed) + } + override fun newState(state: DisclaimerState) = with(binding) { if (activity == null || view == null) return updateUiVisibility(state.disclaimer, state.progressState) - binding.webView.loadUrl(state.disclaimer.getUri().toString()) } private fun updateUiVisibility(disclaimer: Disclaimer, progressState: ProgressState?) = with(binding) { @@ -118,7 +121,9 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs groupError.hide() webView.loadLocalTermsOfServices() } - else -> Unit + else -> { + webView.loadUrl(disclaimer.getUri().toString()) + } } } diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 4d108a3d06..55aa1a6dbb 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -334,7 +334,7 @@ У меня есть промо-код… Tangem Wallet Другие способы оплаты - Все распродано, но не огорчайтесь! Мы уже производим новые партии Tangem Wallet. Чтобы получить ваш кошелек скорее, оформите предзаказ сейчас. Отправка заказов начнется + Из-за большого количества заказов, которые мы получаем, cроки доставки могут быть увеличены. Итого Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату. Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. From 11cafd40a1a03d18e928290e631f8af72d8060a0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 13 Jun 2023 15:38:55 +0800 Subject: [PATCH 32/32] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 06d43ff04b..06b3dd6a6b 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -77,7 +77,7 @@ reactiveNetwork = "3.0.8" # region Tangem tangemBlockchainSdk = "develop-240" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "release-app_4.6-254" +tangemCardSdk = "develop-266" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem