From 9b2becff1d5e6f8041c97c2d61632171aedd99ac Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 Jan 2024 16:11:09 +0300 Subject: [PATCH 1/8] 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 2fcde13f2c..95395aacec 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,9 +85,9 @@ spr-client = "3.6.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-458" +tangemBlockchainSdk = "release-app_5.5-459" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-318" +tangemCardSdk = "release-app_5.5-321" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ # endregion Tangem From 8df9a2f8e2134adf3b58ad215031ff38c82eb539 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 22 Jan 2024 13:40:55 +0300 Subject: [PATCH 2/8] Updated on 2026-08-14 --- .../ui/appsettings/AppSettingsFragment.kt | 44 ++------ .../ui/appsettings/AppSettingsItemsFactory.kt | 21 +++- .../ui/appsettings/AppSettingsViewModel.kt | 106 +++++++++++++----- .../AppSettingsItemsAnalyticsSender.kt | 32 ++++++ .../utils/SelectedWalletAnalyticsSender.kt | 29 +++++ .../utils/WalletWarningsAnalyticsSender.kt | 2 +- .../loaders/WalletContentLoaderFactory.kt | 4 +- .../wallet/viewmodels/WalletViewModelV2.kt | 4 + 8 files changed, 170 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/details/ui/appsettings/analytics/AppSettingsItemsAnalyticsSender.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/SelectedWalletAnalyticsSender.kt diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt index 8afcd15101..61e02daeda 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt @@ -1,21 +1,22 @@ package com.tangem.tap.features.details.ui.appsettings import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.lifecycle.lifecycleScope +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.navigation.NavigationAction import com.tangem.core.ui.screen.ComposeFragment import com.tangem.core.ui.theme.AppThemeModeHolder import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.tap.features.details.redux.DetailsAction -import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.store import dagger.hilt.android.AndroidEntryPoint -import org.rekotlin.StoreSubscriber import javax.inject.Inject @AndroidEntryPoint -internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber { +internal class AppSettingsFragment : ComposeFragment() { @Inject override lateinit var appThemeModeHolder: AppThemeModeHolder @@ -23,43 +24,20 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber().apply { + LocalLifecycleOwner.current.lifecycle.addObserver(observer = this) + } + val state by viewModel.uiState.collectAsStateWithLifecycle() + AppSettingsScreen( modifier = modifier, - state = viewModel.uiState, + state = state, onBackClick = { store.dispatch(DetailsAction.ResetCardSettingsData) store.dispatch(NavigationAction.PopBackTo()) }, ) } - - override fun onResume() { - super.onResume() - viewModel.checkBiometricsStatus(lifecycleScope) - } - - override fun onStart() { - super.onStart() - store.subscribe(this) { state -> - state.skipRepeats { oldState, newState -> - oldState.detailsState == newState.detailsState - }.select { it.detailsState } - } - } - - override fun onStop() { - super.onStop() - store.unsubscribe(this) - } - - override fun newState(state: DetailsState) { - if (activity == null || view == null) return - viewModel.updateState(state) - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt index 0d1f66f8bb..47bbb8376f 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsItemsFactory.kt @@ -10,7 +10,7 @@ internal class AppSettingsItemsFactory { fun createEnrollBiometricsCard(onClick: () -> Unit): Item.Card { return Item.Card( - id = "enroll_biometrics_card", + id = ID_ENROLL_BIOMETRICS_CARD, title = resourceReference(R.string.app_settings_enable_biometrics_title), description = resourceReference(R.string.app_settings_enable_biometrics_description), iconResId = R.drawable.ic_alert_circle_24, @@ -24,7 +24,7 @@ internal class AppSettingsItemsFactory { onCheckedChange: (Boolean) -> Unit, ): Item.Switch { return Item.Switch( - id = "save_wallets_switch", + id = ID_SAVE_WALLETS_SWITCH, title = resourceReference(R.string.app_settings_saved_wallet), description = resourceReference(R.string.app_settings_saved_wallet_footer), isEnabled = isEnabled, @@ -39,7 +39,7 @@ internal class AppSettingsItemsFactory { onCheckedChange: (Boolean) -> Unit, ): Item.Switch { return Item.Switch( - id = "save_access_codes_switch", + id = ID_SAVE_ACCESS_CODES_SWITCH, title = resourceReference(R.string.app_settings_saved_access_codes), description = resourceReference(R.string.app_settings_saved_access_codes_footer), isEnabled = isEnabled, @@ -54,7 +54,7 @@ internal class AppSettingsItemsFactory { onCheckedChange: (Boolean) -> Unit, ): Item.Switch { return Item.Switch( - id = "flip_to_hide_balance_switch", + id = ID_FLIP_TO_HIDE_BALANCE_SWITCH, title = resourceReference(R.string.details_row_title_flip_to_hide), description = resourceReference(R.string.details_row_description_flip_to_hide), isEnabled = isEnabled, @@ -65,7 +65,7 @@ internal class AppSettingsItemsFactory { fun createSelectAppCurrencyButton(currentAppCurrencyName: String, onClick: () -> Unit): Item.Button { return Item.Button( - id = "select_app_currency_button", + id = ID_SELECT_APP_CURRENCY_BUTTON, title = resourceReference(R.string.details_row_title_currency), description = stringReference(currentAppCurrencyName), isEnabled = true, @@ -75,7 +75,7 @@ internal class AppSettingsItemsFactory { fun createSelectThemeModeButton(currentThemeMode: AppThemeMode, onClick: () -> Unit): Item.Button { return Item.Button( - id = "select_theme_mode_button", + id = ID_SELECT_THEME_MODE_BUTTON, title = resourceReference(R.string.app_settings_theme_selector_title), description = resourceReference( id = when (currentThemeMode) { @@ -88,4 +88,13 @@ internal class AppSettingsItemsFactory { onClick = onClick, ) } + + companion object { + const val ID_ENROLL_BIOMETRICS_CARD = "enroll_biometrics_card" + const val ID_SAVE_WALLETS_SWITCH = "save_wallets_switch" + const val ID_SAVE_ACCESS_CODES_SWITCH = "save_access_codes_switch" + const val ID_FLIP_TO_HIDE_BALANCE_SWITCH = "flip_to_hide_balance_switch" + const val ID_SELECT_APP_CURRENCY_BUTTON = "select_app_currency_button" + const val ID_SELECT_THEME_MODE_BUTTON = "select_theme_mode_button" + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt index f2dae0ca71..1a473c49ec 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsViewModel.kt @@ -1,10 +1,10 @@ package com.tangem.tap.features.details.ui.appsettings -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.LifecycleCoroutineScope -import com.tangem.core.analytics.Analytics +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ViewModel +import androidx.lifecycle.lifecycleScope +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.appcurrency.repository.AppCurrencyRepository @@ -13,53 +13,78 @@ import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchWithMain -import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.details.redux.AppSetting import com.tangem.tap.features.details.redux.AppSettingsState import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState +import com.tangem.tap.features.details.ui.appsettings.analytics.AppSettingsItemsAnalyticsSender import com.tangem.tap.scope +import com.tangem.tap.store import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import org.rekotlin.Store +import kotlinx.coroutines.flow.* +import org.rekotlin.StoreSubscriber +import javax.inject.Inject -internal class AppSettingsViewModel( - private val store: Store, +@HiltViewModel +internal class AppSettingsViewModel @Inject constructor( private val appCurrencyRepository: AppCurrencyRepository, -) { + private val analyticsEventHandler: AnalyticsEventHandler, + private val appSettingsItemsAnalyticsSender: AppSettingsItemsAnalyticsSender, +) : ViewModel(), + StoreSubscriber, + DefaultLifecycleObserver { private val itemsFactory = AppSettingsItemsFactory() private val dialogsFactory = AppSettingsDialogsFactory() private val appCurrencyUpdatesJobHolder = JobHolder() - var uiState: AppSettingsScreenState by mutableStateOf(AppSettingsScreenState.Loading) - private set + private val _uiState: MutableStateFlow = MutableStateFlow( + value = AppSettingsScreenState.Loading, + ) + val uiState: StateFlow = _uiState init { bootstrapAppCurrencyUpdates() + + subscribeToStoreChanges() + sendItemsAnalytics() } - fun updateState(state: DetailsState) { - uiState = AppSettingsScreenState.Content( - items = buildItems(state.appSettingsState), - dialog = (uiState as? AppSettingsScreenState.Content)?.dialog, - ) + override fun newState(state: DetailsState) { + val items = buildItems(state.appSettingsState) + + _uiState.update { prevState -> + when (prevState) { + is AppSettingsScreenState.Content -> prevState.copy( + items = items, + ) + is AppSettingsScreenState.Loading -> AppSettingsScreenState.Content( + items = items, + dialog = null, + ) + } + } } - fun checkBiometricsStatus(lifecycleScope: LifecycleCoroutineScope) { - store.dispatch(DetailsAction.AppSettings.CheckBiometricsStatus(lifecycleScope)) + override fun onResume(owner: LifecycleOwner) { + store.dispatch(DetailsAction.AppSettings.CheckBiometricsStatus(owner.lifecycleScope)) + } + + override fun onCleared() { + store.unsubscribe(subscriber = this) } private fun buildItems(state: AppSettingsState): ImmutableList { val items = buildList { if (state.needEnrollBiometrics) { - Analytics.send(Settings.AppSettings.EnableBiometrics) - itemsFactory.createEnrollBiometricsCard(onClick = ::enrollBiometrics).let(::add) + itemsFactory.createEnrollBiometricsCard( + onClick = ::enrollBiometrics, + ).let(::add) } itemsFactory.createSelectAppCurrencyButton( @@ -89,9 +114,10 @@ internal class AppSettingsViewModel( onCheckedChange = ::onFlipToHideBalanceToggled, ).let(::add) - itemsFactory.createSelectThemeModeButton(state.selectedThemeMode) { - showThemeModeSelector(state.selectedThemeMode) - }.let(::add) + itemsFactory.createSelectThemeModeButton( + currentThemeMode = state.selectedThemeMode, + onClick = { showThemeModeSelector(state.selectedThemeMode) }, + ).let(::add) } return items.toImmutableList() @@ -111,7 +137,7 @@ internal class AppSettingsViewModel( dialog = dialogsFactory.createThemeModeSelectorDialog( selectedModeIndex = selectedMode.ordinal, onSelect = { mode -> - Analytics.send( + analyticsEventHandler.send( event = Settings.AppSettings.ThemeSwitched( theme = AnalyticsParam.AppTheme.fromAppThemeMode(mode), ), @@ -167,7 +193,7 @@ internal class AppSettingsViewModel( private fun onFlipToHideBalanceToggled(enable: Boolean) { val param = AnalyticsParam.OnOffState(enable) - Analytics.send(Settings.AppSettings.HideBalanceChanged(param)) + analyticsEventHandler.send(Settings.AppSettings.HideBalanceChanged(param)) store.dispatch(DetailsAction.AppSettings.ChangeBalanceHiding(hideBalance = enable)) } @@ -188,10 +214,28 @@ internal class AppSettingsViewModel( .saveIn(appCurrencyUpdatesJobHolder) } + private fun subscribeToStoreChanges() { + store.subscribe(subscriber = this) { state -> + state.skipRepeats { oldState, newState -> + oldState.detailsState == newState.detailsState + }.select { it.detailsState } + } + } + + private fun sendItemsAnalytics() { + uiState + .filterIsInstance() + .distinctUntilChangedBy(AppSettingsScreenState.Content::items) + .onEach { appSettingsItemsAnalyticsSender.send(it.items) } + .launchIn(scope) + } + private fun updateContentState(block: AppSettingsScreenState.Content.() -> AppSettingsScreenState.Content) { - uiState = when (val state = uiState) { - is AppSettingsScreenState.Content -> block(state) - is AppSettingsScreenState.Loading -> state + _uiState.update { prevState -> + when (prevState) { + is AppSettingsScreenState.Content -> block(prevState) + is AppSettingsScreenState.Loading -> prevState + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/analytics/AppSettingsItemsAnalyticsSender.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/analytics/AppSettingsItemsAnalyticsSender.kt new file mode 100644 index 0000000000..d61f054cb5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/analytics/AppSettingsItemsAnalyticsSender.kt @@ -0,0 +1,32 @@ +package com.tangem.tap.features.details.ui.appsettings.analytics + +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.tap.common.analytics.events.Settings +import com.tangem.tap.features.details.ui.appsettings.AppSettingsItemsFactory +import com.tangem.tap.features.details.ui.appsettings.AppSettingsScreenState +import dagger.hilt.android.scopes.ViewModelScoped +import javax.inject.Inject + +@ViewModelScoped +internal class AppSettingsItemsAnalyticsSender @Inject constructor( + private val analyticsHandler: AnalyticsEventHandler, +) { + + fun send(items: List) { + val events = getEvents(items) + + events.forEach { analyticsHandler.send(it) } + } + + private fun getEvents(items: List): Set { + return items.mapNotNullTo(mutableSetOf(), ::getEvent) + } + + private fun getEvent(item: AppSettingsScreenState.Item): AnalyticsEvent? { + return when (item.id) { + AppSettingsItemsFactory.ID_ENROLL_BIOMETRICS_CARD -> Settings.AppSettings.EnableBiometrics + else -> null + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/SelectedWalletAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/SelectedWalletAnalyticsSender.kt new file mode 100644 index 0000000000..ca5a6989b8 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/SelectedWalletAnalyticsSender.kt @@ -0,0 +1,29 @@ +package com.tangem.feature.wallet.presentation.wallet.analytics.utils + +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent +import javax.inject.Inject + +internal class SelectedWalletAnalyticsSender @Inject constructor( + private val analyticsEventHandler: AnalyticsEventHandler, +) { + + fun send(userWallet: UserWallet) { + val event = getEvent(userWallet) + + if (event != null) { + analyticsEventHandler.send(event) + } + } + + /** + + * that cannot be processed in [WalletWarningsAnalyticsSender]. + * */ + private fun getEvent(userWallet: UserWallet): AnalyticsEvent? = when { + userWallet.isLocked -> WalletScreenAnalyticsEvent.MainScreen.WalletUnlock + else -> null + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt index d28b7fdc06..d8f8fa7457 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt @@ -43,7 +43,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor( is WalletNotification.Informational.DemoCard -> MainScreen.DemoCard is WalletNotification.Informational.MissingAddresses -> MainScreen.MissingAddresses is WalletNotification.RateApp -> MainScreen.HowDoYouLikeTangem - is WalletNotification.UnlockWallets -> MainScreen.WalletUnlock + is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender] is WalletNotification.Informational.NoAccount, is WalletNotification.Warning.LowSignatures, is WalletNotification.Warning.SomeNetworksUnreachable, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletContentLoaderFactory.kt index 224cd27c24..f897350e0e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletContentLoaderFactory.kt @@ -21,7 +21,9 @@ internal class WalletContentLoaderFactory @Inject constructor( isRefresh: Boolean = false, ): WalletContentLoader? { return when { - userWallet.isMultiCurrency -> multiWalletContentLoaderFactory.create(userWallet, clickIntents) + userWallet.isMultiCurrency -> { + multiWalletContentLoaderFactory.create(userWallet, clickIntents) + } userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() -> { singleWalletWithTokenContentLoaderFactory.create(userWallet, clickIntents) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt index fed5c1dc6e..a7bd5b9f0b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt @@ -15,6 +15,7 @@ import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent.DemonstrateWalletsScrollPreview.Direction @@ -55,6 +56,7 @@ internal class WalletViewModelV2 @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, private val reduxStateHolder: ReduxStateHolder, private val screenLifecycleProvider: ScreenLifecycleProvider, + private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender, ) : ViewModel() { val uiState: StateFlow = stateHolder.uiState @@ -152,6 +154,8 @@ internal class WalletViewModelV2 @Inject constructor( reduxStateHolder.dispatch( action = WalletConnectActions.New.SetupUserChains(userWallet = selectedWallet), ) + + selectedWalletAnalyticsSender.send(selectedWallet) } } .flowOn(dispatchers.main) From e5107de19bf4f1f212fcea1e8d1cc26eb4f13b96 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 22 Jan 2024 18:59:52 +0500 Subject: [PATCH 3/8] Updated on 2026-08-14 --- .../main/java/com/tangem/datasource/config/ConfigManagerImpl.kt | 1 - .../main/java/com/tangem/datasource/config/models/JsonModels.kt | 1 - gradle/dependencies.toml | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt index 364e4941d6..ed2d89b8d2 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt @@ -124,7 +124,6 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager { gnosis = GetBlockAccessToken(jsonRpc = accessTokens.gnosis?.jsonRPC), cronos = GetBlockAccessToken(jsonRpc = accessTokens.cronos?.jsonRPC), solana = GetBlockAccessToken(jsonRpc = accessTokens.solana?.jsonRPC), - stellar = GetBlockAccessToken(rest = accessTokens.stellar?.rest), ton = GetBlockAccessToken(jsonRpc = accessTokens.ton?.jsonRPC), tron = GetBlockAccessToken(rest = accessTokens.tron?.rest), cosmos = GetBlockAccessToken(rest = accessTokens.cosmos?.rest), diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt index bc4f50bc30..ec2d4845ad 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt @@ -57,7 +57,6 @@ data class GetBlockAccessTokens( @Json(name = "xdai") val gnosis: GetBlockToken?, @Json(name = "cronos") val cronos: GetBlockToken?, @Json(name = "solana") val solana: GetBlockToken?, - @Json(name = "stellar") val stellar: GetBlockToken?, @Json(name = "ton") val ton: GetBlockToken?, @Json(name = "tron") val tron: GetBlockToken?, @Json(name = "cosmos-hub") val cosmos: GetBlockToken?, diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 95395aacec..1a5289de27 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ spr-client = "3.6.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.5-459" +tangemBlockchainSdk = "release-app_5.5-460" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.5-321" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 1813cd06a9a1eaab4b421405ac834b7a3567a929 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 22 Jan 2024 16:56:27 +0300 Subject: [PATCH 4/8] Updated on 2026-08-14 --- .../data/converters/CoinsResponseConverter.kt | 2 + .../feature/swap/DefaultSwapRepository.kt | 13 ------ .../swap/converters/TokensConverter.kt | 40 ------------------- .../feature/swap/domain/api/SwapRepository.kt | 5 --- 4 files changed, 2 insertions(+), 58 deletions(-) delete mode 100644 features/swap/data/src/main/java/com/tangem/feature/swap/converters/TokensConverter.kt diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/CoinsResponseConverter.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/CoinsResponseConverter.kt index 35d8f6c47a..b611e0a092 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/CoinsResponseConverter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/CoinsResponseConverter.kt @@ -25,6 +25,8 @@ internal object CoinsResponseConverter : Converter> { networks = token.networks.mapNotNull { network -> val blockchain = Blockchain.fromNetworkId(network.networkId) ?: return@mapNotNull null + if (!blockchain.canHandleTokens()) return@mapNotNull null + Token.Network( id = network.networkId, blockchain = blockchain, diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt index 45ee2c0517..6bcbabd302 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt @@ -55,7 +55,6 @@ internal class DefaultSwapRepository @Inject constructor( moshi: Moshi, ) : SwapRepository { - private val tokensConverter = TokensConverter() private val expressDataConverter = ExpressDataConverter() private val leastTokenInfoConverter = LeastTokenInfoConverter() private val swapPairInfoConverter = SwapPairInfoConverter() @@ -213,18 +212,6 @@ internal class DefaultSwapRepository @Inject constructor( } } - override suspend fun getExchangeableTokens(networkId: String): List { - return withContext(coroutineDispatcher.io) { - tokensConverter.convertList( - tangemTechApi.getCoins( - exchangeable = true, - active = true, - networkIds = networkId, - ).getOrThrow().coins, - ) - } - } - override suspend fun findBestQuote( fromContractAddress: String, fromNetwork: String, diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/TokensConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/TokensConverter.kt deleted file mode 100644 index b5f5743fe3..0000000000 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/TokensConverter.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.tangem.feature.swap.converters - -import com.tangem.datasource.api.tangemTech.models.CoinsResponse -import com.tangem.feature.swap.domain.models.domain.Currency -import com.tangem.utils.converter.Converter - -class TokensConverter : Converter { - - override fun convert(value: CoinsResponse.Coin): Currency { - val network = value.networks.first() - return if (network.contractAddress != null && network.decimalCount != null) { - Currency.NonNativeToken( - id = value.id, - name = value.name, - symbol = value.symbol, - networkId = network.networkId, - contractAddress = network.contractAddress!!, - decimalCount = network.decimalCount!!.intValueExact(), - logoUrl = getSmallIconUrl(value.id), - ) - } else { - Currency.NativeToken( - id = value.id, - name = value.name, - symbol = value.symbol, - networkId = network.networkId, - logoUrl = getSmallIconUrl(value.id), - ) - } - } - - private fun getSmallIconUrl(coin: String): String { - return "$DEFAULT_IMAGE_HOST$LARGE_ICON_PATH/$coin.png" - } - - companion object { - private const val DEFAULT_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/" - private const val LARGE_ICON_PATH = "large" - } -} \ No newline at end of file diff --git a/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt b/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt index 727d622c7d..340162c44c 100644 --- a/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt +++ b/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt @@ -17,11 +17,6 @@ interface SwapRepository { suspend fun getRates(currencyId: String, tokenIds: List): Map - /** - * @throws com.tangem.datasource.api.common.response.ApiResponseError - * */ - suspend fun getExchangeableTokens(networkId: String): List - suspend fun getExchangeStatus(txId: String): Either @Suppress("LongParameterList") From ea935d10f22aa33c44be98fccd5de1408b560d21 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 22 Jan 2024 16:36:00 +0300 Subject: [PATCH 5/8] Updated on 2026-08-14 --- .../com/tangem/tap/common/extensions/Navigation.kt | 4 ++-- .../tap/features/onboarding/OnboardingHelper.kt | 13 ++++++++----- ...sCardsFragment.kt => OnboardingTwinsFragment.kt} | 2 +- .../wallet/viewmodels/WalletViewModelV2.kt | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) rename app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/{TwinsCardsFragment.kt => OnboardingTwinsFragment.kt} (99%) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index df5762ec97..6dc414e945 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -20,7 +20,7 @@ import com.tangem.tap.features.home.HomeFragment import com.tangem.tap.features.main.ui.ModalNotificationBottomSheetFragment import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment -import com.tangem.tap.features.onboarding.products.twins.ui.TwinsCardsFragment +import com.tangem.tap.features.onboarding.products.twins.ui.OnboardingTwinsFragment import com.tangem.tap.features.onboarding.products.wallet.ui.OnboardingWalletFragment import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment import com.tangem.tap.features.send.ui.SendFragment @@ -125,7 +125,7 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.Shop -> ShopFragment() AppScreen.OnboardingNote -> OnboardingNoteFragment() AppScreen.OnboardingWallet -> OnboardingWalletFragment() - AppScreen.OnboardingTwins -> TwinsCardsFragment() + AppScreen.OnboardingTwins -> OnboardingTwinsFragment() AppScreen.OnboardingOther -> OnboardingOtherCardsFragment() AppScreen.Wallet -> { store.state.daggerGraphState diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index 015bf0d5d6..fa2b2a0832 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -4,6 +4,7 @@ import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.Basic import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.util.cardTypesResolver @@ -14,7 +15,6 @@ import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.tap.* import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter -import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.extensions.removeContext @@ -106,14 +106,17 @@ object OnboardingHelper { backupCardsIds = backupCardsIds?.toSet(), ), ) + store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet)) + delay(timeMillis = 1_800) store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.SaveWallet)) } // If device has no biometry and save wallet screen has been shown, then go through old scenario - else -> proceedWithScanResponse(scanResponse, backupCardsIds) + else -> { + proceedWithScanResponse(scanResponse, backupCardsIds) + store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet)) + } } } - - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet)) } fun onInterrupted() { @@ -143,7 +146,7 @@ object OnboardingHelper { Timber.e(error, "Unable to save user wallet") } .doOnSuccess { - scope.launch { store.onUserWalletSelected(userWallet) } + mainScope.launch { store.onUserWalletSelected(userWallet) } } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/OnboardingTwinsFragment.kt similarity index 99% rename from app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt rename to app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/OnboardingTwinsFragment.kt index e07b4ab7f3..75e54a693c 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/OnboardingTwinsFragment.kt @@ -39,7 +39,7 @@ import javax.inject.Inject @Suppress("LargeClass") @AndroidEntryPoint -class TwinsCardsFragment : BaseOnboardingFragment() { +internal class OnboardingTwinsFragment : BaseOnboardingFragment() { @Inject lateinit var assetReader: AssetReader diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt index a7bd5b9f0b..ae097c7991 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt @@ -112,8 +112,8 @@ internal class WalletViewModelV2 @Inject constructor( private fun subscribeToUserWalletsUpdates(shouldSaveUserWallet: Boolean) { getWalletsUseCase() - .distinctUntilChanged() .conflate() + .distinctUntilChanged() .map { walletsUpdateActionResolver.resolve( wallets = it, From 0c4a135923bff9b0e4cb20baefd5c892f3324929 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 23 Jan 2024 12:06:45 +0300 Subject: [PATCH 6/8] Updated on 2026-08-14 --- .../ui/dialogs/AttestationFailedDialog.kt | 4 +-- .../feature/swap/router/CustomTabsManager.kt | 32 +++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/AttestationFailedDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/AttestationFailedDialog.kt index ef9363f8a0..3e9df5885e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/AttestationFailedDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/AttestationFailedDialog.kt @@ -11,8 +11,8 @@ internal object AttestationFailedDialog { fun create(context: Context): AlertDialog { return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - setTitle(R.string.attestation_online_failed_title) - setMessage(R.string.attestation_online_failed_body) + setTitle(R.string.common_error) + setMessage(R.string.issuer_signature_loading_failed) setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/router/CustomTabsManager.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/router/CustomTabsManager.kt index 15e39f08f3..565d27a3b8 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/router/CustomTabsManager.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/router/CustomTabsManager.kt @@ -1,18 +1,38 @@ package com.tangem.feature.swap.router import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager import android.net.Uri import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent import java.lang.ref.WeakReference class CustomTabsManager(private val context: WeakReference) { + fun openUrl(url: String) { - val customTabsIntent = CustomTabsIntent.Builder() - .setDefaultColorSchemeParams( - CustomTabColorSchemeParams.Builder().build(), - ) - .build() - context.get()?.let { customTabsIntent.launchUrl(it, Uri.parse(url)) } + val mContext = context.get() ?: return + val browserIntent = Intent() + .setAction(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_BROWSABLE) + .setDataAndType(Uri.fromParts("http", "", null), "text/plain") + var possibleBrowsers = + mContext.packageManager.queryIntentActivities(browserIntent, PackageManager.MATCH_DEFAULT_ONLY) + if (possibleBrowsers.isEmpty()) { + possibleBrowsers = + mContext.packageManager.queryIntentActivities(browserIntent, PackageManager.MATCH_ALL) + } + if (possibleBrowsers.isNotEmpty()) { + val customTabsIntent = CustomTabsIntent.Builder() + .setDefaultColorSchemeParams( + CustomTabColorSchemeParams.Builder().build(), + ) + .build() + customTabsIntent.intent.setPackage(possibleBrowsers[0].activityInfo.packageName) + context.get()?.let { customTabsIntent.launchUrl(it, Uri.parse(url)) } + } else { + val browserIntent2 = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + mContext.startActivity(browserIntent2) + } } } \ No newline at end of file From c2d547b33d49d87bbcae31f0c267534dff4e1b25 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 22 Jan 2024 17:16:50 +0300 Subject: [PATCH 7/8] 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 0965959789..4d2d187879 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 @@ -919,7 +919,7 @@ internal class SwapInteractorImpl @Inject constructor( return when { fromToken is CryptoCurrency.Token -> { - if (feeValue > tokenForFeeBalance.value) { + if (feeValue > tokenForFeeBalance.value || tokenForFeeBalance.value.signum() == 0) { IncludeFeeInAmount.BalanceNotEnough } else { IncludeFeeInAmount.Excluded From 0eb8735da422f5bebb5146767945515b9a319d08 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 23 Jan 2024 13:27:03 +0300 Subject: [PATCH 8/8] 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 1a5289de27..75863084e4 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ spr-client = "3.6.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_5.5-460" +tangemBlockchainSdk = "release-app_5.5-462" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "release-app_5.5-321" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^