From a75226b865ab0663068fac897aac65468f079bbb Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 20 Apr 2022 13:09:57 +0300 Subject: [PATCH] Updated on 2026-08-14 --- app/build.gradle | 28 ++++-- .../com/tangem/tap/common/DialogManager.kt | 1 + .../java/com/tangem/tap/common/TestActions.kt | 75 +++++++++++++++ .../tap/common/extensions/WalletManager.kt | 4 +- .../tangem/tap/common/redux/StateDialog.kt | 5 + .../compose/AddCustomTokenScreen.kt | 3 +- .../TestAddCostomTokenActions.kt} | 6 +- .../wallet/ui/WalletDetailsFragment.kt | 26 +++-- .../ui/test/TestWalletDetailsActions.kt | 94 +++++++++++++++++++ 9 files changed, 222 insertions(+), 20 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/TestActions.kt rename app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/{DebugActions.kt => test/TestAddCostomTokenActions.kt} (98%) create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWalletDetailsActions.kt diff --git a/app/build.gradle b/app/build.gradle index 78402e567b..bd37eb2e39 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,11 @@ apply plugin: 'kotlin-kapt' apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' +ext.configVar = [ + configEnvironment: "CONFIG_ENVIRONMENT", + testActionEnabled: "TEST_ACTION_ENABLED", +] + android { compileSdkVersion 31 defaultConfig { @@ -13,19 +18,14 @@ android { minSdkVersion 21 targetSdkVersion 31 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + buildConfigField 'Boolean', configVar.testActionEnabled, 'false' } buildFeatures { compose true viewBinding true } buildTypes { - release { - debuggable false - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.debug - buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"prod\"' - } debug { debuggable true minifyEnabled false @@ -34,14 +34,24 @@ android { firebaseCrashlytics { mappingFileUploadEnabled false } - buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"dev\"' + buildConfigField 'String', configVar.configEnvironment, '\"dev\"' + buildConfigField 'Boolean', configVar.testActionEnabled, 'true' } debug_beta { initWith release debuggable false versionNameSuffix "-beta" applicationIdSuffix ".debug" - buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"prod\"' + buildConfigField 'String', configVar.configEnvironment, '\"prod\"' + buildConfigField 'Boolean', configVar.testActionEnabled, 'false' + } + release { + debuggable false + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.debug + buildConfigField 'String', configVar.configEnvironment, '\"prod\"' + buildConfigField 'Boolean', configVar.testActionEnabled, 'false' } } kotlinOptions { diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index 56ce4dd062..ac00ee4f88 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -53,6 +53,7 @@ class DialogManager : StoreSubscriber { is AppDialog.SimpleOkDialogRes -> SimpleOkDialog.create(state.dialog, context) is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) + is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context) is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context) is WalletConnectDialog.UnsupportedCard -> SimpleAlertDialog.create( diff --git a/app/src/main/java/com/tangem/tap/common/TestActions.kt b/app/src/main/java/com/tangem/tap/common/TestActions.kt new file mode 100644 index 0000000000..d5939dae64 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/TestActions.kt @@ -0,0 +1,75 @@ +package com.tangem.tap.common + +import android.content.Context +import android.os.Bundle +import android.view.View +import android.widget.Button +import androidx.appcompat.widget.LinearLayoutCompat +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.common.extensions.dispatchDialogShow +import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.redux.AppDialog +import com.tangem.tap.store +import com.tangem.wallet.BuildConfig + +/** +[REDACTED_AUTHOR] + */ + +object TestActions { + + // It used only for the test actions in debug or debug_beta builds + var testAmountInjectionForWalletManagerEnabled = false + + /** + * @param isTestView - true must be used if you want to show or hide your view depends on BuildConfig + */ + fun initFor(view: View, actions: List, isTestView: Boolean = false) { + if (isTestView) view.show(BuildConfig.TEST_ACTION_ENABLED) + if (!BuildConfig.TEST_ACTION_ENABLED) return + + view.setOnClickListener { + store.dispatchDialogShow(AppDialog.TestActionsDialog(actions)) + } + } +} + +typealias TestAction = Pair Unit> + +class TestActionsBottomSheetDialog( + private val appDialog: AppDialog.TestActionsDialog, + context: Context +) : BottomSheetDialog(context) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(createContentView()) + behavior.state = BottomSheetBehavior.STATE_EXPANDED + behavior.peekHeight = 400 + setOnCancelListener { + store.dispatchDialogHide() + } + } + + private fun createContentView(): View { + val actionButtons = mutableListOf() + appDialog.actionsList.forEach { (actionName, action) -> + val view = Button(context).apply { + text = actionName + setOnClickListener { + action() + cancel() + } + } + actionButtons.add(view) + } + + return LinearLayoutCompat(context).apply { + orientation = LinearLayoutCompat.VERTICAL + actionButtons.forEach { addView(it) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt index 0adf835be5..540d23c3f7 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -4,6 +4,7 @@ import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager import com.tangem.common.services.Result +import com.tangem.tap.common.TestActions import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.amountToCreateAccount import com.tangem.tap.domain.getFirstToken @@ -20,8 +21,9 @@ import timber.log.Timber [REDACTED_AUTHOR] */ suspend fun WalletManager.safeUpdate(): Result = try { - if (isDemoWallet()) { + if (isDemoWallet() || TestActions.testAmountInjectionForWalletManagerEnabled) { delay(500) + TestActions.testAmountInjectionForWalletManagerEnabled = false Result.Success(wallet) } else { update() diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt index 6419e09ac2..77f99b84b5 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt @@ -1,6 +1,7 @@ package com.tangem.tap.common.redux import com.tangem.common.extensions.VoidCallback +import com.tangem.tap.common.TestAction import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.features.wallet.redux.Currency @@ -17,4 +18,8 @@ sealed class AppDialog : StateDialog { val currency: Currency, val addressData: AddressData, ) : AppDialog() + + data class TestActionsDialog( + val actionsList: List + ) : AppDialog() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenScreen.kt b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenScreen.kt index 8463eba5ac..d9736ae8d0 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/AddCustomTokenScreen.kt @@ -26,6 +26,7 @@ import com.tangem.tap.common.compose.ComposeDialogManager import com.tangem.tap.common.compose.ToggledRippleTheme import com.tangem.tap.common.compose.keyboardAsState import com.tangem.tap.common.moduleMessage.ModuleMessageConverter +import com.tangem.tap.features.tokens.addCustomToken.compose.test.TestAddCustomTokenActions import com.tangem.wallet.R /** @@ -51,7 +52,7 @@ fun AddCustomTokenScreen(state: MutableState) { LazyColumn( contentPadding = PaddingValues(bottom = 90.dp) ) { - item { AddCustomTokenDebugActions() } + item { TestAddCustomTokenActions() } item { Surface( modifier = Modifier.padding(16.dp), diff --git a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/DebugActions.kt b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/test/TestAddCostomTokenActions.kt similarity index 98% rename from app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/DebugActions.kt rename to app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/test/TestAddCostomTokenActions.kt index 1bbbce94ce..981e2a1b74 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/DebugActions.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/addCustomToken/compose/test/TestAddCostomTokenActions.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.tokens.addCustomToken.compose +package com.tangem.tap.features.tokens.addCustomToken.compose.test import androidx.compose.foundation.layout.* import androidx.compose.material.Button @@ -25,8 +25,8 @@ import timber.log.Timber [REDACTED_AUTHOR] */ @Composable -fun AddCustomTokenDebugActions() { - if (!BuildConfig.DEBUG) return +fun TestAddCustomTokenActions() { + if (!BuildConfig.TEST_ACTION_ENABLED) return Column() { // deep test diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index 1006df572c..2c1fa686cd 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -15,6 +15,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding import com.squareup.picasso.Picasso import com.tangem.common.extensions.guard import com.tangem.tap.common.SnackbarHandler +import com.tangem.tap.common.TestActions import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.NavigationAction @@ -24,6 +25,7 @@ import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog +import com.tangem.tap.features.wallet.ui.test.TestWalletDetails import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.FragmentWalletDetailsBinding @@ -68,12 +70,11 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), (activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar) binding.toolbar.setNavigationOnClickListener { activity?.onBackPressed() } - setupTransactionsRecyclerView() setupButtons() + setupTestActionButton() } - private fun setupTransactionsRecyclerView() = with(binding) { pendingTransactionAdapter = PendingTransactionsAdapter() rvPendingTransaction.layoutManager = LinearLayoutManager(requireContext()) @@ -91,6 +92,15 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), btnSell.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Sell) } } + private fun setupTestActionButton() { + view?.findViewById(R.id.l_balance)?.let { view -> + TestActions.initFor( + view = view, + actions = TestWalletDetails.solanaRentExemptWarning() + ) + } + } + override fun newState(state: WalletState) { if (activity == null || view == null) return if (state.selectedCurrency == null) return @@ -111,11 +121,13 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), binding.srlWalletDetails.setOnRefreshListener { if (selectedWallet.currencyData.status != BalanceStatus.Loading) { - store.dispatch( - WalletAction.LoadWallet( - blockchain = BlockchainNetwork(selectedWallet.currency.blockchain, selectedWallet.currency.derivationPath, emptyList()) + store.dispatch(WalletAction.LoadWallet( + blockchain = BlockchainNetwork( + selectedWallet.currency.blockchain, + selectedWallet.currency.derivationPath, + emptyList() ) - ) + )) } } @@ -163,6 +175,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), } private fun handleNotEnoughFundsOnMainCurrency(selectedWalletData: WalletData) = with(binding) { + if (selectedWalletData.currency.isBlockchain()) return@with + if (selectedWalletData.shouldShowCoinAmountWarning()) { val blockchainName = selectedWalletData.currency.blockchain.fullName val warningMessage = requireContext().getString( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWalletDetailsActions.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWalletDetailsActions.kt new file mode 100644 index 0000000000..1c32663fc8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWalletDetailsActions.kt @@ -0,0 +1,94 @@ +package com.tangem.tap.features.wallet.ui.test + +import com.tangem.blockchain.blockchains.solana.SolanaWalletManager +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.Blockchain +import com.tangem.domain.common.ScanResponse +import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.tap.common.TestAction +import com.tangem.tap.common.TestActions +import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.store +import java.math.BigDecimal +import kotlin.random.Random + +/** +[REDACTED_AUTHOR] + */ +class TestWalletDetails { + companion object { + fun solanaRentExemptWarning(): List { + val checker = SolanaRentWarningActionEmitter() + return listOf( + "BALANCE = 0.0" to { checker.setZeroBalance() }, + "BALANCE < 0.00089088" to { checker.setLessThanRentExempt() }, + "BALANCE > 0.00089088" to { checker.setMoreThanRentExempt() }, + "BALANCE = 0.00089087" to { checker.setLessThanRentExemptByOne() }, + "BALANCE = 0.00089088 (rent exempt)" to { checker.setForRentExemptBarrier() }, + "BALANCE = 0.00089089" to { checker.setMoreThanRentExemptByOne() }, + ) + } + } +} + +private class SolanaRentWarningActionEmitter { + + private val zero = BigDecimal.ZERO + private val one = BigDecimal(0.00000001) + private val rentExemptBarrier = BigDecimal(0.00089088) + private val lessThanRentExemptByOne = rentExemptBarrier.minus(one) + private val moreThanRentExemptByOne = rentExemptBarrier.plus(one) + private val moreThanRentExempt = rentExemptBarrier.plus(Random.nextDouble().toBigDecimal()) + private val lessThanRentExempt = rentExemptBarrier + .minus(Random.nextDouble(0.0, rentExemptBarrier.minus(one).toDouble()).toBigDecimal()) + + fun setZeroBalance() { + setBalance(zero) + } + + fun setForRentExemptBarrier() { + setBalance(rentExemptBarrier) + } + + fun setLessThanRentExemptByOne() { + setBalance(lessThanRentExemptByOne) + } + + fun setMoreThanRentExemptByOne() { + setBalance(moreThanRentExemptByOne) + } + + fun setMoreThanRentExempt() { + setBalance(moreThanRentExempt) + } + + fun setLessThanRentExempt() { + setBalance(lessThanRentExempt) + } + + private fun setBalance(value: BigDecimal) { + val amount = Amount(getBlockchain()).copy(value = value) + getWalletManager().apply { + TestActions.testAmountInjectionForWalletManagerEnabled = true + wallet.setAmount(amount) + } + store.dispatch(WalletAction.LoadData) + } + + private fun getWalletManager(): SolanaWalletManager { + return store.state.walletState.getWalletManager(BlockchainNetwork( + blockchain = getBlockchain(), + card = scanResponse().card + )) as SolanaWalletManager + } + + private fun getBlockchain(): Blockchain = when { + scanResponse().card.isTestCard -> Blockchain.SolanaTestnet + else -> Blockchain.Solana + } + + private fun scanResponse(): ScanResponse { + return store.state.globalState.scanResponse ?: throw UnsupportedOperationException() + } +} \ No newline at end of file