From 648d4a98b81db3133e110e4d4f89fd4a749aab30 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 14 Jan 2021 15:43:14 +0300 Subject: [PATCH 01/29] Updated on 2026-08-14 --- app/build.gradle | 1 + .../main/java/com/tangem/tap/TapApplication.kt | 16 +++------------- .../tangem/tap/domain/config/ConfigLoader.kt | 17 +++++++++++++++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 433a84d15c..c5b70f368e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,6 +40,7 @@ android { initWith debug versionNameSuffix "-beta" applicationIdSuffix ".debug" + buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"prod\"' } } kotlinOptions { diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 5e43828af0..cf26114c15 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -1,13 +1,11 @@ package com.tangem.tap import android.app.Application -import com.google.firebase.ktx.Firebase -import com.google.firebase.remoteconfig.ktx.remoteConfig -import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.tangem.tap.common.images.PicassoHelper import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.appReducer import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.domain.config.ConfigLoader import com.tangem.tap.domain.config.ConfigManager import com.tangem.tap.domain.config.LocalLoader import com.tangem.tap.domain.config.RemoteLoader @@ -29,17 +27,9 @@ class TapApplication : Application() { override fun onCreate() { super.onCreate() - if (BuildConfig.DEBUG) { - Timber.plant(Timber.DebugTree()) - Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings { - this.minimumFetchIntervalInSeconds = 60 - }) - } else { - Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings { - this.minimumFetchIntervalInSeconds = 3600 - }) - } + if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) + ConfigLoader.init() NetworkConnectivity.createInstance(store, this) preferencesStorage = PreferencesStorage(this) PicassoHelper.initPicassoWithCaching(this) diff --git a/app/src/main/java/com/tangem/tap/domain/config/ConfigLoader.kt b/app/src/main/java/com/tangem/tap/domain/config/ConfigLoader.kt index 738f7fd99f..2e90f4cb16 100644 --- a/app/src/main/java/com/tangem/tap/domain/config/ConfigLoader.kt +++ b/app/src/main/java/com/tangem/tap/domain/config/ConfigLoader.kt @@ -3,6 +3,7 @@ package com.tangem.tap.domain.config import android.content.Context import com.google.firebase.ktx.Firebase import com.google.firebase.remoteconfig.ktx.remoteConfig +import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler @@ -18,13 +19,25 @@ interface ConfigLoader { companion object { const val featuresName = "features_${BuildConfig.CONFIG_ENVIRONMENT}" const val configValuesName = "config_${BuildConfig.CONFIG_ENVIRONMENT}" + + fun init() { + if (BuildConfig.CONFIG_ENVIRONMENT == "prod") { + Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings { + this.minimumFetchIntervalInSeconds = 1200 + }) + } else { + Firebase.remoteConfig.setConfigSettingsAsync(remoteConfigSettings { + this.minimumFetchIntervalInSeconds = 60 + }) + } + } } } class LocalLoader( private val context: Context, - private val moshi: Moshi + private val moshi: Moshi, ) : ConfigLoader { override fun loadConfig(onComplete: (ConfigModel) -> Unit) { @@ -49,7 +62,7 @@ class LocalLoader( } class RemoteLoader( - private val moshi: Moshi + private val moshi: Moshi, ) : ConfigLoader { override fun loadConfig(onComplete: (ConfigModel) -> Unit) { From f01122698f52748b4a618bebd28df3194be75c58 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 12 Feb 2021 13:39:04 +0300 Subject: [PATCH 02/29] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 66c4238850..207062e770 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -128,6 +128,11 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable Date: Fri, 12 Feb 2021 13:50:22 +0300 Subject: [PATCH 03/29] Updated on 2026-08-14 --- app/src/main/assets/features_dev.json | 2 +- app/src/main/assets/features_prod.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/assets/features_dev.json b/app/src/main/assets/features_dev.json index 4866065376..71561c4c69 100644 --- a/app/src/main/assets/features_dev.json +++ b/app/src/main/assets/features_dev.json @@ -1,6 +1,6 @@ { "isWalletPayIdEnabled": true, - "isTopUpEnabled": true, "isSendingToPayIdEnabled": true, + "isTopUpEnabled": true, "isCreatingTwinCardsAllowed": false } \ No newline at end of file diff --git a/app/src/main/assets/features_prod.json b/app/src/main/assets/features_prod.json index 7c4ebbbb72..d3b1375fae 100644 --- a/app/src/main/assets/features_prod.json +++ b/app/src/main/assets/features_prod.json @@ -1,6 +1,6 @@ { - "isWalletPayIdEnabled": true, - "isTopUpEnabled": true, + "isWalletPayIdEnabled": false, "isSendingToPayIdEnabled": true, + "isTopUpEnabled": true, "isCreatingTwinCardsAllowed": true } \ No newline at end of file From 7a64811bcbe53fff3a5e838dcae46996064a0483 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 15 Feb 2021 11:34:10 +0300 Subject: [PATCH 04/29] Updated on 2026-08-14 --- app/src/main/res/layout/layout_wallet_long_buttons.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/layout/layout_wallet_long_buttons.xml b/app/src/main/res/layout/layout_wallet_long_buttons.xml index 3872698afc..d32b3ac917 100644 --- a/app/src/main/res/layout/layout_wallet_long_buttons.xml +++ b/app/src/main/res/layout/layout_wallet_long_buttons.xml @@ -10,6 +10,7 @@ android:layout_width="0dp" android:layout_marginStart="16dp" android:layout_marginTop="30dp" + android:layout_marginBottom="33dp" android:text="@string/wallet_button_scan" app:icon="@drawable/ic_scan" app:layout_constraintBottom_toBottomOf="parent" @@ -34,6 +35,7 @@ android:layout_marginStart="8dp" android:layout_marginTop="30dp" android:layout_marginEnd="16dp" + android:layout_marginBottom="33dp" android:text="@string/wallet_button_send" app:icon="@drawable/ic_send" app:layout_constraintBottom_toBottomOf="parent" From b0a2fe8dddfab1f75c0aa3ceb439bc8f134e4e12 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 16 Feb 2021 22:52:43 +0300 Subject: [PATCH 05/29] Updated on 2026-08-14 --- .../features/details/redux/twins/CreateTwinWalletMiddleware.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt index 38cfac39bc..669ab9be91 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt @@ -18,7 +18,7 @@ class CreateTwinWalletMiddleware { fun handle(action: DetailsAction.CreateTwinWalletAction) { when (action) { is DetailsAction.CreateTwinWalletAction.ShowWarning -> { - val wallet = store.state.detailsState.wallet + val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet if (wallet == null) { store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning)) return From e31a60bd427cb8fac6948ca339e542b43ab4ac73 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 Feb 2021 11:19:37 +0300 Subject: [PATCH 06/29] Updated on 2026-08-14 --- .../ui/stateSubscribers/SendStateSubscriber.kt | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 8c0c8fdf23..3476f1ae19 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -177,22 +177,6 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val amountToSend = state.viewAmountValue if (!amountToSend.isFromUserInput) fg.etAmountToSend.update(amountToSend.value) -// fg.tvAmountToSendShadow.text = amountToSend -// if (amountToSend.length > 10) { -// post is needed to wait for text size changes -// fg.tvAmountToSendShadow.post { -// fg.etAmountToSend.setTextSize(TypedValue.COMPLEX_UNIT_PX, fg.tvAmountToSendShadow.textSize - 2) -// fg.etAmountToSend.update(amountToSend) -// if (!state.cursorAtTheSamePosition) fg.etAmountToSend.setSelection(amountToSend.length) -// } -// } else { -// val textSize = fg.resources.getDimension(R.dimen.text_size_amount_to_send) -// fg.tvAmountToSendShadow.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) -// fg.etAmountToSend.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) -// fg.etAmountToSend.update(amountToSend) -// if (!state.cursorAtTheSamePosition) fg.etAmountToSend.setSelection(amountToSend.length) -// } - fg.tvAmountCurrency.update(state.mainCurrency.currencySymbol) (fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.type) From 03276ea8404f22d49d573484d6e532bf957dd26e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 Feb 2021 11:33:30 +0300 Subject: [PATCH 07/29] Updated on 2026-08-14 --- .../send/redux/middlewares/AddressPayIdMiddleware.kt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index ded3984926..900c790986 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt @@ -1,19 +1,17 @@ package com.tangem.tap.features.send.redux.middlewares +import androidx.core.text.isDigitsOnly import com.tangem.blockchain.common.Wallet import com.tangem.commands.common.network.Result import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.isPayIdSupported -import com.tangem.tap.features.send.redux.AddressPayIdActionUi -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction +import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification.SetAddressError import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification.SetWalletAddress import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdError import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdWalletAddress -import com.tangem.tap.features.send.redux.FeeAction -import com.tangem.tap.features.send.redux.TransactionExtrasAction import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.Dispatchers @@ -50,7 +48,7 @@ internal class AddressPayIdMiddleware { private fun setAddressAndCheck(data: String, isUserInput: Boolean, dispatch: (Action) -> Unit) { val potentialPayId = data.toLowerCase() - if (PayIdManager.isPayId(potentialPayId) && isPayIdEnabled()) { + if (isPayIdEnabled() && PayIdManager.isPayId(potentialPayId)) { dispatch(SetPayIdWalletAddress(potentialPayId, "", isUserInput)) } else { dispatch(SetWalletAddress(data, isUserInput)) @@ -64,7 +62,7 @@ internal class AddressPayIdMiddleware { val addressPayId = sendState.addressPayIdState.normalFieldValue ?: return val isUserInput = sendState.addressPayIdState.viewFieldValue.isFromUserInput - if (PayIdManager.isPayId(addressPayId) && isPayIdEnabled()) { + if (isPayIdEnabled() && PayIdManager.isPayId(addressPayId)) { verifyPayId(addressPayId, wallet, isUserInput, dispatch) } else { verifyAddress(addressPayId, wallet, isUserInput, dispatch) From 176d3d5ce19b85f881f5f6825999e04811b862b8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 Feb 2021 11:34:37 +0300 Subject: [PATCH 08/29] Updated on 2026-08-14 --- .../middlewares/AddressPayIdMiddleware.kt | 20 ++++++++++++------- .../tap/features/send/ui/SendFragment.kt | 11 +++++++--- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index 900c790986..e9beba0bc0 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt @@ -129,6 +129,9 @@ internal class AddressPayIdMiddleware { val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, supposedAddress) if (failReason == null) { + noSchemeAddress.getQueryParameter("amount")?.let { + if (it.isDigitsOnly()) dispatch(AmountAction.SetAmount(it.toBigDecimal(), false)) + } dispatch(SetWalletAddress(supposedAddress, isUserInput)) dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, null)) } else { @@ -149,14 +152,10 @@ internal class AddressPayIdMiddleware { } } - //TODO: move to the blockchainSDK - private fun extractAddressFromShareUri(shareUri: String): String { - val sharePrefix = listOf("bitcoin:", "ethereum:", "xrpl:", "litecoin:", "bnb:") - val prefixes = sharePrefix.filter { shareUri.contains(it) } - return if (prefixes.isEmpty()) shareUri else shareUri.replace(prefixes[0], "") - } - private fun String.removeShareUriQuery(): String = this.substringBefore("?") + private fun String.getQueryParameter(name: String): String? { + return this.substringAfter("?").splitToMap("&", "=")[name] + } private fun verifyClipboard(input: String?, appState: AppState?, dispatch: DispatchFunction) { val addressPayId = input ?: return @@ -184,4 +183,11 @@ internal class AddressPayIdMiddleware { private fun isPayIdEnabled(): Boolean { return store.state.globalState.configManager?.config?.isSendingToPayIdEnabled ?: false } +} + +fun String.splitToMap(firstDelimiter: String, secondDelimiter: String): Map { + return this.split(firstDelimiter) + .map { it.split(secondDelimiter) } + .map { it.first() to it.last().toString() } + .toMap() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 874e744207..6dae105162 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -139,9 +139,14 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { val scannedCode = data?.getStringExtra(ScanQrCodeActivity.SCAN_RESULT) ?: "" if (scannedCode.isEmpty()) return - store.dispatch(PasteAddressPayId(scannedCode)) - store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) - store.dispatch(FeeAction.RequestFee) + // Delayed launch is needed in order for the UI to be drawn and to process the sent events. + // If do not use the delay, then etAmount error field is not displayed when + // inserting an incorrect amount by shareUri + imvQrCode.postDelayed({ + store.dispatch(PasteAddressPayId(scannedCode)) + store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) + store.dispatch(FeeAction.RequestFee) + }, 200) } private fun setupAmountLayout() { From cc1b9636d2537628e3f1175a019be81cfe4d73dd Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 Feb 2021 10:47:44 +0300 Subject: [PATCH 09/29] Updated on 2026-08-14 --- .../tap/domain/termsOfUse/TermsOfUse.kt | 52 +++++++++++++++++++ .../features/details/redux/DetailsAction.kt | 2 + .../features/details/redux/DetailsReducer.kt | 3 +- .../features/details/redux/DetailsState.kt | 3 +- .../features/details/ui/DetailsFragment.kt | 9 ++++ .../tap/features/send/ui/SendFragment.kt | 3 ++ .../tap/features/wallet/ui/WalletFragment.kt | 2 + app/src/main/res/layout/fragment_details.xml | 17 +++++- .../main/res/values/strings_untranslated.xml | 1 + 9 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/termsOfUse/TermsOfUse.kt diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/TermsOfUse.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/TermsOfUse.kt new file mode 100644 index 0000000000..7facc2c92f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/termsOfUse/TermsOfUse.kt @@ -0,0 +1,52 @@ +package com.tangem.tap.domain.termsOfUse + +import android.content.res.Resources +import androidx.core.os.ConfigurationCompat +import java.util.* + +/** +[REDACTED_AUTHOR] + */ +class TermsOfUse { + val locale: Locale = ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0) + + fun getUrl(cardId: String): String? { + val terms = CardTermsFactory.create(cardId) ?: return null + + return terms.getUrl(locale.country) + } +} + +class CardTermsOfUse( + private val urlsByRegion: Map, + private val defaultRegion: String, +) { + + fun getUrl(region: String): String? { + return urlsByRegion[region.toLowerCase()] ?: urlsByRegion[defaultRegion] + } +} + + +class CardTermsFactory { + companion object { + + fun create(cardId: String): CardTermsOfUse? { + val batch = cardId.substring(0..3) + + return when (batch) { + "CB05" -> { + CardTermsOfUse( + mapOf( + "ru" to "https://yandex.ru/", + "en" to "https://www.google.com/", + "fr" to "https://www.qwant.com", + ), + "ru" + ) + } + else -> null + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 2ec73c0f00..14fc7582a8 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -6,6 +6,7 @@ import com.tangem.commands.common.card.Card import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.termsOfUse.TermsOfUse import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.features.details.redux.twins.CreateTwinWallet import com.tangem.tap.network.coinmarketcap.FiatCurrency @@ -19,6 +20,7 @@ sealed class DetailsAction : Action { val scanNoteResponse: ScanNoteResponse, val wallet: Wallet?, val isCreatingTwinWalletAllowed: Boolean?, + val termsOfUse: TermsOfUse, val fiatCurrencyName: FiatCurrencyName, val fiatCurrencies: List? = null, ) : DetailsAction() diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 805d31f8c9..c26028d141 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -73,7 +73,8 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta action.fiatCurrencyName ), securityScreenState = SecurityScreenState(currentOption = securityOption), - createTwinWalletState = twinsState + createTwinWalletState = twinsState, + cardTermsOfUseUrl = action.termsOfUse.getUrl(action.card.cardId) ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 4730507ebc..774ecfd6c6 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -18,7 +18,8 @@ data class DetailsState( val eraseWalletState: EraseWalletState? = null, val confirmScreenState: ConfirmScreenState? = null, val securityScreenState: SecurityScreenState? = null, - val createTwinWalletState: CreateTwinWalletState? = null + val createTwinWalletState: CreateTwinWalletState? = null, + val cardTermsOfUseUrl: String? = null ) : StateType data class CardInfo( diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 262482b360..caa9780539 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -1,5 +1,7 @@ package com.tangem.tap.features.details.ui +import android.content.Intent +import android.net.Uri import android.os.Bundle import android.view.View import androidx.activity.OnBackPressedCallback @@ -80,6 +82,13 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber + + + app:layout_constraintTop_toBottomOf="@id/tv_card_tou" /> Invalid destination tag. It won\'t be added to the transaction Invalid Memo ID. It won\'t be added to the transaction + Terms of use From 1f6b9ac469348ecd36aa8c4e644900455d42d843 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 Feb 2021 10:48:37 +0300 Subject: [PATCH 10/29] Updated on 2026-08-14 --- app/src/main/res/layout/fragment_details.xml | 2 +- app/src/main/res/values/strings_untranslated.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 95475088a1..b2ac116aef 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -139,7 +139,7 @@ app:drawableEndCompat="@drawable/ic_angle_bracket_right" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_disclaimer" - android:text="@string/terms_of_use" /> + android:text="@string/details_terms_of_use" /> The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning. Tap the card #%s You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet. + Terms of use The twin address was successfully created @@ -38,6 +39,5 @@ this wallet. Invalid destination tag. It won\'t be added to the transaction Invalid Memo ID. It won\'t be added to the transaction - Terms of use From d4490a76ba96aecd270a7e138873d81fb8657c34 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 Feb 2021 12:48:10 +0300 Subject: [PATCH 11/29] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/TapApplication.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index c2ada9667f..6497a63edc 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -1,6 +1,9 @@ package com.tangem.tap import android.app.Application +import com.google.firebase.ktx.Firebase +import com.google.firebase.remoteconfig.ktx.remoteConfig +import com.google.firebase.remoteconfig.ktx.remoteConfigSettings import com.tangem.tap.common.images.PicassoHelper import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.appReducer @@ -39,7 +42,6 @@ class TapApplication : Application() { }) } - ConfigLoader.init() NetworkConnectivity.createInstance(store, this) preferencesStorage = PreferencesStorage(this) PicassoHelper.initPicassoWithCaching(this) From 5c5d8910147c5c2d1c0a8e532dfe3adcef5febcb Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 20 Feb 2021 15:04:52 +0300 Subject: [PATCH 12/29] Updated on 2026-08-14 --- .../tangem/tap/domain/termsOfUse/CardTou.kt | 46 ++++++++++++++++ .../tap/domain/termsOfUse/TermsOfUse.kt | 52 ------------------- .../features/details/redux/DetailsAction.kt | 4 +- .../features/details/redux/DetailsReducer.kt | 2 +- .../features/details/redux/DetailsState.kt | 7 +-- .../features/details/ui/DetailsFragment.kt | 3 +- .../tap/features/send/BaseStoreFragment.kt | 4 ++ .../tap/features/send/ui/SendFragment.kt | 3 -- .../tap/features/wallet/ui/WalletFragment.kt | 4 +- app/src/main/res/layout/fragment_details.xml | 2 +- .../main/res/values/strings_untranslated.xml | 2 +- 11 files changed, 62 insertions(+), 67 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt delete mode 100644 app/src/main/java/com/tangem/tap/domain/termsOfUse/TermsOfUse.kt diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt new file mode 100644 index 0000000000..a6050e243c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt @@ -0,0 +1,46 @@ +package com.tangem.tap.domain.termsOfUse + +import android.content.res.Resources +import android.net.Uri +import androidx.core.os.ConfigurationCompat +import com.tangem.commands.common.card.Card +import java.util.* + +/** +[REDACTED_AUTHOR] + */ +class CardTou { + private val locale: Locale = ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0) + + fun getUrl(card: Card): Uri? { + val issuerName = card.cardData?.issuerName ?: return null + if (issuerName.toLowerCase() != "start2coin") return null + + val baseUrl = "https://app.tangem.com/tou/" + val regionCode = regionCode(card.cardId) ?: "fr" + val filename = filename(locale.language, regionCode) + return Uri.parse(baseUrl + filename) + } + + private fun filename(languageCode: String, regionCode: String): String { + return when { + languageCode == "fr" && regionCode == "ch" -> "Start2Coin-fr-ch-tangem.pdf" + languageCode == "de" && regionCode == "ch" -> "Start2Coin-de-ch-tangem.pdf" + languageCode == "en" && regionCode == "ch" -> "Start2Coin-en-ch-tangem.pdf" + languageCode == "it" && regionCode == "ch" -> "Start2Coin-it-ch-tangem.pdf" + languageCode == "fr" && regionCode == "fr" -> "Start2Coin-fr-fr-atangem.pdf" + languageCode == "de" && regionCode == "at" -> "Start2Coin-de-at-tangem.pdf" + languageCode.isEmpty() && regionCode == "fr" -> "Start2Coin-fr-fr-atangem.pdf" + languageCode.isEmpty() && regionCode == "ch" -> "Start2Coin-en-ch-tangem.pdf" + languageCode.isEmpty() && regionCode == "at" -> "Start2Coin-de-at-tangem.pdf" + else -> "Start2Coin-fr-fr-atangem.pdf" + } + } + + private fun regionCode(cardId: String): String? = when (cardId.substring(2..3)) { + "0" -> "fr" + "1" -> "ch" + "2" -> "at" + else -> null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/TermsOfUse.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/TermsOfUse.kt deleted file mode 100644 index 7facc2c92f..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/termsOfUse/TermsOfUse.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.tangem.tap.domain.termsOfUse - -import android.content.res.Resources -import androidx.core.os.ConfigurationCompat -import java.util.* - -/** -[REDACTED_AUTHOR] - */ -class TermsOfUse { - val locale: Locale = ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0) - - fun getUrl(cardId: String): String? { - val terms = CardTermsFactory.create(cardId) ?: return null - - return terms.getUrl(locale.country) - } -} - -class CardTermsOfUse( - private val urlsByRegion: Map, - private val defaultRegion: String, -) { - - fun getUrl(region: String): String? { - return urlsByRegion[region.toLowerCase()] ?: urlsByRegion[defaultRegion] - } -} - - -class CardTermsFactory { - companion object { - - fun create(cardId: String): CardTermsOfUse? { - val batch = cardId.substring(0..3) - - return when (batch) { - "CB05" -> { - CardTermsOfUse( - mapOf( - "ru" to "https://yandex.ru/", - "en" to "https://www.google.com/", - "fr" to "https://www.qwant.com", - ), - "ru" - ) - } - else -> null - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 14fc7582a8..65a2c7dff6 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -6,7 +6,7 @@ import com.tangem.commands.common.card.Card import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.domain.termsOfUse.TermsOfUse +import com.tangem.tap.domain.termsOfUse.CardTou import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.features.details.redux.twins.CreateTwinWallet import com.tangem.tap.network.coinmarketcap.FiatCurrency @@ -20,7 +20,7 @@ sealed class DetailsAction : Action { val scanNoteResponse: ScanNoteResponse, val wallet: Wallet?, val isCreatingTwinWalletAllowed: Boolean?, - val termsOfUse: TermsOfUse, + val cardTou: CardTou, val fiatCurrencyName: FiatCurrencyName, val fiatCurrencies: List? = null, ) : DetailsAction() diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index c26028d141..ebb0485c03 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -74,7 +74,7 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta ), securityScreenState = SecurityScreenState(currentOption = securityOption), createTwinWalletState = twinsState, - cardTermsOfUseUrl = action.termsOfUse.getUrl(action.card.cardId) + cardTermsOfUseUrl = action.cardTou.getUrl(action.card) ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 774ecfd6c6..7995acdcdb 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.details.redux +import android.net.Uri import com.tangem.blockchain.common.Wallet import com.tangem.commands.common.card.Card import com.tangem.tap.common.entities.Button @@ -19,13 +20,13 @@ data class DetailsState( val confirmScreenState: ConfirmScreenState? = null, val securityScreenState: SecurityScreenState? = null, val createTwinWalletState: CreateTwinWalletState? = null, - val cardTermsOfUseUrl: String? = null + val cardTermsOfUseUrl: Uri? = null, ) : StateType data class CardInfo( val cardId: String, val issuer: String, - val signedHashes: Int + val signedHashes: Int, ) enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty } @@ -34,7 +35,7 @@ data class SecurityScreenState( val currentOption: SecurityOption = SecurityOption.LongTap, val selectedOption: SecurityOption = currentOption, val allowedOptions: EnumSet = EnumSet.allOf(SecurityOption::class.java), - val buttonProceed: Button = Button(true) + val buttonProceed: Button = Button(true), ) enum class SecurityOption { LongTap, PassCode, AccessCode } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index caa9780539..8578a60304 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.details.ui import android.content.Intent -import android.net.Uri import android.os.Bundle import android.view.View import androidx.activity.OnBackPressedCallback @@ -85,7 +84,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber + android:text="@string/details_row_title_card_tou" /> The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning. Tap the card #%s You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet. - Terms of use + Card terms of use The twin address was successfully created From 16b756ad883d95e38b9fa5dd9130d2348a497d9a Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 20 Feb 2021 15:08:58 +0300 Subject: [PATCH 13/29] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt index a6050e243c..8ffcf14108 100644 --- a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt +++ b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt @@ -30,9 +30,9 @@ class CardTou { languageCode == "it" && regionCode == "ch" -> "Start2Coin-it-ch-tangem.pdf" languageCode == "fr" && regionCode == "fr" -> "Start2Coin-fr-fr-atangem.pdf" languageCode == "de" && regionCode == "at" -> "Start2Coin-de-at-tangem.pdf" - languageCode.isEmpty() && regionCode == "fr" -> "Start2Coin-fr-fr-atangem.pdf" - languageCode.isEmpty() && regionCode == "ch" -> "Start2Coin-en-ch-tangem.pdf" - languageCode.isEmpty() && regionCode == "at" -> "Start2Coin-de-at-tangem.pdf" + regionCode == "fr" -> "Start2Coin-fr-fr-atangem.pdf" + regionCode == "ch" -> "Start2Coin-en-ch-tangem.pdf" + regionCode == "at" -> "Start2Coin-de-at-tangem.pdf" else -> "Start2Coin-fr-fr-atangem.pdf" } } From e346c755c6410c0024d698cf869b1040cd808eb5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 24 Feb 2021 15:40:56 +0300 Subject: [PATCH 14/29] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt | 8 ++++---- .../send/redux/middlewares/AddressPayIdMiddleware.kt | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt index 8ffcf14108..c5d9d91396 100644 --- a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt +++ b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt @@ -37,10 +37,10 @@ class CardTou { } } - private fun regionCode(cardId: String): String? = when (cardId.substring(2..3)) { - "0" -> "fr" - "1" -> "ch" - "2" -> "at" + private fun regionCode(cardId: String): String? = when (cardId[1]) { + '0' -> "fr" + '1' -> "ch" + '2' -> "at" else -> null } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index e9beba0bc0..b5793affa8 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt @@ -1,6 +1,5 @@ package com.tangem.tap.features.send.redux.middlewares -import androidx.core.text.isDigitsOnly import com.tangem.blockchain.common.Wallet import com.tangem.commands.common.network.Result import com.tangem.tap.common.redux.AppState @@ -129,8 +128,8 @@ internal class AddressPayIdMiddleware { val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, supposedAddress) if (failReason == null) { - noSchemeAddress.getQueryParameter("amount")?.let { - if (it.isDigitsOnly()) dispatch(AmountAction.SetAmount(it.toBigDecimal(), false)) + noSchemeAddress.getQueryParameter("amount")?.toBigDecimalOrNull()?.let { + dispatch(AmountAction.SetAmount(it, false)) } dispatch(SetWalletAddress(supposedAddress, isUserInput)) dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, null)) From 354660e1169b704335668a29953a15a4eb3a0241 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 24 Feb 2021 19:10:50 +0300 Subject: [PATCH 15/29] Updated on 2026-08-14 --- .../send/redux/middlewares/AddressPayIdMiddleware.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index b5793affa8..a713ed3163 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.send.redux.middlewares +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Wallet import com.tangem.commands.common.network.Result import com.tangem.tap.common.redux.AppState @@ -107,7 +108,11 @@ internal class AddressPayIdMiddleware { } private fun verifyAddress(address: String, wallet: Wallet, isUserInput: Boolean, dispatch: (Action) -> Unit) { - val addressSchemeSplit = address.split(":") + val addressSchemeSplit = if (wallet.blockchain == Blockchain.BitcoinCash) { + listOf(address) + } else { + address.split(":") + } val noSchemeAddress = when (addressSchemeSplit.size) { 1 -> address // no scheme 2 -> { // scheme @@ -141,7 +146,7 @@ internal class AddressPayIdMiddleware { private fun isValidBlockchainAddressAndNotTheSameAsWallet(wallet: Wallet, address: String): Error? { return if (wallet.blockchain.validateAddress(address)) { - if (wallet.addresses.all { it.value != address } ) { + if (wallet.addresses.all { it.value != address }) { null } else { Error.ADDRESS_SAME_AS_WALLET From 2ba24e17dfcfaf2456c465e331e26315df598f55 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 24 Feb 2021 22:35:56 +0300 Subject: [PATCH 16/29] Updated on 2026-08-14 --- app/build.gradle | 2 +- .../configurable/config/ConfigManager.kt | 19 ++++++++++++++----- .../domain/configurable/config/JsonModels.kt | 3 +++ .../tangem/tap/domain/tasks/ScanNoteTask.kt | 10 ++++++++-- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 549774d931..d2a3c6f2f5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,7 +72,7 @@ dependencies { implementation 'com.google.android.material:material:1.2.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1' - implementation 'com.tangem:blockchain:1.141.0' + implementation 'com.tangem:blockchain:1.143.0' implementation 'com.tangem:core:1.101.0' implementation 'com.tangem:sdk:1.101.0' diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index 67025b175d..e9ba4a8e30 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -1,5 +1,6 @@ package com.tangem.tap.domain.configurable.config +import com.tangem.blockchain.common.BlockchainSdkConfig import com.tangem.tangem_sdk_new.ui.animation.VoidCallback import com.tangem.tap.domain.configurable.Loader @@ -10,6 +11,7 @@ data class Config( val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de", val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE", val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C", + val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), val isWalletPayIdEnabled: Boolean = true, val isSendingToPayIdEnabled: Boolean = true, val isTopUpEnabled: Boolean = false, @@ -78,12 +80,22 @@ class ConfigManager( config = config.copy( coinMarketCapKey = values.coinMarketCapKey, moonPayApiKey = values.moonPayApiKey, - moonPayApiSecretKey = values.moonPayApiSecretKey + moonPayApiSecretKey = values.moonPayApiSecretKey, + blockchainSdkConfig = BlockchainSdkConfig( + blockchairApiKey = values.blockchairApiKey, + blockcypherTokens = values.blockcypherTokens, + infuraProjectId = values.infuraProjectId + ) ) defaultConfig = defaultConfig.copy( coinMarketCapKey = values.coinMarketCapKey, moonPayApiKey = values.moonPayApiKey, - moonPayApiSecretKey = values.moonPayApiSecretKey + moonPayApiSecretKey = values.moonPayApiSecretKey, + blockchainSdkConfig = BlockchainSdkConfig( + blockchairApiKey = values.blockchairApiKey, + blockcypherTokens = values.blockcypherTokens, + infuraProjectId = values.infuraProjectId + ) ) } @@ -92,8 +104,5 @@ class ConfigManager( const val isSendingToPayIdEnabled = "isSendingToPayIdEnabled" const val isCreatingTwinCardsAllowed = "isCreatingTwinCardsAllowed" const val isTopUpEnabled = "isTopUpEnabled" - const val coinMarketCapKey = "coinMarketCapKey" - const val moonPayApiKey = "moonPayApiKey" - const val moonPayApiSecretKey = "moonPayApiSecretKey" } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt index 32ce46da8b..69c5f0a8b0 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt @@ -15,6 +15,9 @@ class ConfigValueModel( val coinMarketCapKey: String, val moonPayApiKey: String, val moonPayApiSecretKey: String, + val blockchairApiKey: String?, + val blockcypherTokens: Set?, + val infuraProjectId: String?, ) class ConfigModel(val features: FeatureModel?, val configValues: ConfigValueModel?) { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 207062e770..e5b438cc80 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -4,6 +4,7 @@ import com.tangem.CardSession import com.tangem.CardSessionRunnable import com.tangem.TangemError import com.tangem.TangemSdkError +import com.tangem.blockchain.common.BlockchainSdkConfig import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.commands.CommandResponse @@ -18,6 +19,7 @@ import com.tangem.common.extensions.toHexString import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.store import com.tangem.tasks.ScanTask data class ScanNoteResponse( @@ -30,6 +32,10 @@ data class ScanNoteResponse( class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { override val requiresPin2 = false + private val blockchainSdkConfig = store.state.globalState.configManager?.config + ?.blockchainSdkConfig ?: BlockchainSdkConfig() + private val walletManagerFactory = WalletManagerFactory(blockchainSdkConfig) + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { ScanTask().run(session) { result -> when (result) { @@ -53,7 +59,7 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable Date: Mon, 1 Mar 2021 15:46:04 +0300 Subject: [PATCH 17/29] Updated on 2026-08-14 --- app/build.gradle | 2 + .../java/com/tangem/tap/TapApplication.kt | 17 +- .../tap/common/redux/global/GlobalAction.kt | 10 +- .../common/redux/global/GlobalMidlleware.kt | 10 +- .../tap/common/redux/global/GlobalReducer.kt | 3 + .../tap/common/redux/global/GlobalState.kt | 2 + .../com/tangem/tap/domain/TapWalletManager.kt | 52 +++-- .../warningMessage/WarningMessage.kt | 4 +- .../warningMessage/WarningMessagesManager.kt | 17 +- .../features/details/ui/DetailsFragment.kt | 5 + .../tap/features/feedback/FeedbackManager.kt | 210 ++++++++++++++++++ .../features/send/redux/SendScreenAction.kt | 4 +- .../send/redux/middlewares/SendMiddleware.kt | 36 ++- .../send/redux/reducers/SendScreenReducer.kt | 3 +- .../ui/dialogs/SendTransactionFailsDialog.kt | 30 +++ .../send/ui/dialogs/TezosWarningDialog.kt | 4 +- .../stateSubscribers/SendStateSubscriber.kt | 9 +- .../tap/features/wallet/redux/WalletAction.kt | 25 ++- .../features/wallet/redux/WalletMiddleware.kt | 28 ++- .../features/wallet/redux/WalletReducer.kt | 16 +- .../tap/features/wallet/redux/WalletState.kt | 4 +- .../tap/features/wallet/ui/WalletFragment.kt | 8 +- .../wallet/ui/WarningMessagesAdapter.kt | 51 ++++- .../wallet/ui/dialogs/ScanFailsDialog.kt | 30 +++ .../tap/persistence/PreferencesStorage.kt | 40 +++- app/src/main/res/layout/fragment_details.xml | 15 +- app/src/main/res/layout/layout_warning.xml | 52 ++++- .../main/res/values/strings_untranslated.xml | 20 +- 28 files changed, 647 insertions(+), 60 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt create mode 100644 app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt diff --git a/app/build.gradle b/app/build.gradle index 549774d931..6135c65a84 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -70,6 +70,8 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'com.google.android.material:material:1.2.1' + implementation 'com.google.android.play:core:1.9.1' + implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1' implementation 'com.tangem:blockchain:1.141.0' diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 6497a63edc..73538b649c 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -4,6 +4,7 @@ import android.app.Application import com.google.firebase.ktx.Firebase import com.google.firebase.remoteconfig.ktx.remoteConfig import com.google.firebase.remoteconfig.ktx.remoteConfigSettings +import com.tangem.Log import com.tangem.tap.common.images.PicassoHelper import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.appReducer @@ -13,6 +14,9 @@ import com.tangem.tap.domain.configurable.config.FeaturesLocalLoader import com.tangem.tap.domain.configurable.config.FeaturesRemoteLoader import com.tangem.tap.domain.configurable.warningMessage.RemoteWarningLoader import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager +import com.tangem.tap.features.feedback.AdditionalEmailInfo +import com.tangem.tap.features.feedback.FeedbackManager +import com.tangem.tap.features.feedback.TangemLogCollector import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.network.createMoshi import com.tangem.tap.persistence.PreferencesStorage @@ -46,10 +50,10 @@ class TapApplication : Application() { preferencesStorage = PreferencesStorage(this) PicassoHelper.initPicassoWithCaching(this) + initFeedbackManager() loadConfigs() } - private fun loadConfigs() { val moshi = createMoshi() val localLoader = FeaturesLocalLoader(this, moshi) @@ -59,4 +63,15 @@ class TapApplication : Application() { val warningsManager = WarningMessagesManager(RemoteWarningLoader(moshi)) warningsManager.load { store.dispatch(GlobalAction.SetWarningManager(warningsManager)) } } + + private fun initFeedbackManager() { + val infoHolder = AdditionalEmailInfo() + infoHolder.updateAppVersion(this) + + val logWriter = TangemLogCollector() + Log.addLogger(logWriter) + + val feedbackManager = FeedbackManager(infoHolder, this, logWriter) + store.dispatch(GlobalAction.SetFeedbackManager(feedbackManager)) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index f47245676d..2f11ea5a0c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -5,6 +5,8 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.details.redux.SecurityOption +import com.tangem.tap.features.feedback.EmailData +import com.tangem.tap.features.feedback.FeedbackManager import org.rekotlin.Action import java.math.BigDecimal @@ -19,8 +21,12 @@ sealed class GlobalAction : Action { data class Success(val appCurrency: FiatCurrencyName) : GlobalAction() } data class UpdateWalletSignedHashes(val walletSignedHashes: Int?) : GlobalAction() - data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() - data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction() data class HideWarningMessage(val warning: WarningMessage) : GlobalAction() data class UpdateSecurityOptions(val securityOption: SecurityOption) : GlobalAction() + + data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() + data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction() + data class SetFeedbackManager(val feedbackManager: FeedbackManager): GlobalAction() + + data class SendFeedback(val emailData: EmailData): GlobalAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index a0534fe8af..421b48f0f6 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -26,11 +26,17 @@ val globalMiddleware: Middleware = { dispatch, appState -> store.dispatch(WalletAction.SaveCardId) } - store.dispatch(WalletAction.SetWarnings(it.getWarnings(WarningMessage.Location.MainScreen))) - store.dispatch(SendAction.SetWarnings(it.getWarnings(WarningMessage.Location.SendScreen))) + store.dispatch(WalletAction.Warnings.SetWarnings( + it.getWarnings(WarningMessage.Location.MainScreen))) + store.dispatch(SendAction.SetWarnings( + it.getWarnings(WarningMessage.Location.SendScreen))) } } } + + is GlobalAction.SendFeedback -> { + store.state.globalState.feedbackManager?.send(action.emailData) + } } nextDispatch(action) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index afbb6d5b7e..1ca2642b58 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -57,6 +57,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState { globalState } } + is GlobalAction.SetFeedbackManager -> { + globalState.copy(feedbackManager = action.feedbackManager) + } else -> globalState } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 2ee123a725..dc6d6e64ff 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -7,6 +7,7 @@ import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.features.feedback.FeedbackManager import com.tangem.tap.network.coinmarketcap.CoinMarketCapService import org.rekotlin.StateType import java.math.BigDecimal @@ -20,6 +21,7 @@ data class GlobalState( val conversionRates: ConversionRates = ConversionRates(emptyMap()), val configManager: ConfigManager? = null, val warningManager: WarningMessagesManager? = null, + val feedbackManager: FeedbackManager? = null, val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY ) : StateType diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 8ffe1480c6..3b96280599 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -84,23 +84,10 @@ class TapWalletManager { FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card) } TapWorkarounds.updateCard(data.card) - store.state.globalState.warningManager?.setBlockchain(data.walletManager?.wallet?.blockchain) - val configManager = store.state.globalState.configManager - if (TapWorkarounds.isStart2Coin) { - configManager?.turnOff(ConfigManager.isWalletPayIdEnabled) - configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled) - configManager?.turnOff(ConfigManager.isTopUpEnabled) - } else if (data.walletManager?.wallet?.blockchain == Blockchain.Bitcoin - || data.card.cardData?.blockchainName == Blockchain.Bitcoin.id){ - configManager?.turnOff(ConfigManager.isWalletPayIdEnabled) - configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled) - configManager?.resetToDefault(ConfigManager.isTopUpEnabled) - } else { - configManager?.resetToDefault(ConfigManager.isWalletPayIdEnabled) - configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled) - configManager?.resetToDefault(ConfigManager.isTopUpEnabled) - } + updateConfigManager(data) + updateFeedbackManager(data) + withContext(Dispatchers.Main) { store.dispatch(WalletAction.ResetState) store.dispatch(GlobalAction.SaveScanNoteResponse(data)) @@ -117,9 +104,40 @@ class TapWalletManager { } } + private fun updateConfigManager(data: ScanNoteResponse) { + val configManager = store.state.globalState.configManager + if (TapWorkarounds.isStart2Coin) { + configManager?.turnOff(ConfigManager.isWalletPayIdEnabled) + configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled) + configManager?.turnOff(ConfigManager.isTopUpEnabled) + } else if (data.walletManager?.wallet?.blockchain == Blockchain.Bitcoin + || data.card.cardData?.blockchainName == Blockchain.Bitcoin.id) { + configManager?.turnOff(ConfigManager.isWalletPayIdEnabled) + configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled) + configManager?.resetToDefault(ConfigManager.isTopUpEnabled) + } else { + configManager?.resetToDefault(ConfigManager.isWalletPayIdEnabled) + configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled) + configManager?.resetToDefault(ConfigManager.isTopUpEnabled) + } + } + + private fun updateFeedbackManager(data: ScanNoteResponse) { + val card = data.card + val wallet = data.walletManager?.wallet ?: return + val infoHolder = store.state.globalState.feedbackManager?.infoHolder ?: return + + infoHolder.cardId = card.cardId + infoHolder.cardFirmwareVersion = card.firmwareVersion.version + infoHolder.signedHashesCount = card.walletSignedHashes?.toString() ?: "0" + infoHolder.sourceAddress = wallet.address + infoHolder.explorerLink = wallet.getExploreUrl(wallet.address) + infoHolder.blockchain = wallet.blockchain + } + suspend fun loadData(data: ScanNoteResponse) { withContext(Dispatchers.Main) { - store.dispatch(WalletAction.CheckIfWarningNeeded) + store.dispatch(WalletAction.Warnings.CheckIfNeeded) val artworkId = data.verifyResponse?.artworkInfo?.id if (data.walletManager != null) { if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt index 0a8f66677e..ab94c0d563 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt @@ -40,7 +40,9 @@ data class WarningMessage( Permanent, // нельзя скрыть @Json(name = "temporary") - Temporary // можно скрыть (кнопка ОК) + Temporary, // можно скрыть (кнопка ОК) + + AppRating } enum class Location { diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt index 4855b20499..1ee151033f 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt @@ -51,7 +51,8 @@ class WarningMessagesManager( val foundWarning = findWarning(warning) return when { foundWarning == null -> false - foundWarning.type == WarningMessage.Type.Temporary -> { + foundWarning.type == WarningMessage.Type.Temporary + || foundWarning.type == WarningMessage.Type.AppRating -> { if (foundWarning.isHidden) { false } else { @@ -101,7 +102,19 @@ class WarningMessagesManager( WarningMessage.Origin.Local ) - fun isAlreadySignedHashesWarning(warning: WarningMessage):Boolean { + fun appRatingWarning(): WarningMessage = WarningMessage( + "", + "", + WarningMessage.Type.AppRating, + WarningMessage.Priority.Info, + listOf(WarningMessage.Location.MainScreen), + null, + R.string.warning_rate_app_title, + R.string.warning_rate_app_message, + WarningMessage.Origin.Local + ) + + fun isAlreadySignedHashesWarning(warning: WarningMessage): Boolean { return warning.messageResId == R.string.alert_card_signed_transactions } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 262482b360..8c17a4707d 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -7,6 +7,7 @@ import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.getTwinCardIdForUser import com.tangem.tap.domain.twins.isTwinCard @@ -14,6 +15,7 @@ import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.details.redux.twins.CreateTwinWallet +import com.tangem.tap.features.feedback.FeedbackEmail import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_details.* @@ -107,6 +109,9 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber() + + override fun e(logTag: String, message: String) {} + override fun i(logTag: String, message: String) {} + override fun v(logTag: String, message: String) {} + + override fun write(message: LogMessage) { + logs.add(message.message) + } + + fun getLogs(): List = logs.toList() + + fun clearLogs() { + logs.clear() + } +} + +class AdditionalEmailInfo { + var cardId: String = "" + var cardFirmwareVersion: String = "" + var blockchain: Blockchain = Blockchain.Unknown + + var phoneModel: String = Build.MODEL + var osVersion: String = Build.VERSION.SDK_INT.toString() + var appVersion: String = "" + + var token: String = "" + var sourceAddress: String = "" + var destinationAddress: String = "" + var amount: String = "" + var fee: String = "" + + // var transactionHex: String = "" + var signedHashesCount: String = "" + var explorerLink: String = "" +// var outputsCount: String = "" + + fun updateAppVersion(context: Context) { + try { + val pInfo = context.packageManager.getPackageInfo(context.packageName, 0) + appVersion = pInfo.versionName + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + } +} + +interface EmailData { + val subject: String + val mainMessage: String + fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String + + fun joinTogether(infoHolder: AdditionalEmailInfo): String { + return "$mainMessage\n\n\n\n\n" + + "Following information is optional. You can erase it if you don’t want to share it.\n" + + createOptionalMessage(infoHolder) + } +} + +class RateCanBeBetterEmail : EmailData { + override val subject: String = "My suggestions" + override val mainMessage: String = "Tell us what functions you are missing, and we will try to help you." + + override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String { + return StringBuilder().apply { + appendKeyValue("Card ID", infoHolder.cardId) + appendKeyValue("Blockchain", infoHolder.blockchain.fullName) + appendKeyValue("Phone model", infoHolder.phoneModel) + appendKeyValue("OS version", infoHolder.osVersion) + appendKeyValue("App version", infoHolder.appVersion) + }.toString() + } +} + +class ScanFailsEmail : EmailData { + override val subject: String = "Can’t scan a card" + override val mainMessage: String = "Please tell us what card do you have?" + override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String { + return StringBuilder().apply { + appendKeyValue("Phone model", infoHolder.phoneModel) + appendKeyValue("OS version", infoHolder.osVersion) + appendKeyValue("App version", infoHolder.appVersion) + }.toString() + } +} + +class SendTransactionFailedEmail(private val error: String) : EmailData { + override val subject: String = "Can’t send a transaction" + override val mainMessage: String = "Please tell us more about your issue. Every small detail can help." + override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String { + return StringBuilder().apply { + appendKeyValue("Error", error) + appendKeyValue("Card ID", infoHolder.cardId) + appendKeyValue("Blockchain", infoHolder.blockchain.fullName) + appendKeyValue("Token", infoHolder.token) + appendKeyValue("Source address", infoHolder.sourceAddress) + appendKeyValue("Destination address", infoHolder.destinationAddress) + appendKeyValue("Amount", infoHolder.amount) + appendKeyValue("Fee", infoHolder.fee) + appendKeyValue("Phone model", infoHolder.phoneModel) + appendKeyValue("OS version", infoHolder.osVersion) + appendKeyValue("App version", infoHolder.appVersion) + appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion) +// appendKeyValue("Transaction HEX", infoHolder.transactionHex) + }.toString() + } +} + +class FeedbackEmail : EmailData { + override val subject: String = "Tangem Tap feedback" + override val mainMessage: String = "Hi Tangem," + override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String { + return StringBuilder().apply { + appendKeyValue("Card ID", infoHolder.cardId) + appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion) + appendKeyValue("Signed hashes", infoHolder.signedHashesCount) + appendKeyValue("Blockchain", infoHolder.blockchain.fullName) + appendKeyValue("Wallet address", infoHolder.sourceAddress) + appendKeyValue("Explorer link", infoHolder.explorerLink) +// appendKeyValue("Outputs count", infoHolder.outputsCount) + appendKeyValue("Phone model", infoHolder.phoneModel) + appendKeyValue("OS version", infoHolder.osVersion) + }.toString() + } +} + +fun StringBuilder.appendKeyValue(key: String, value: String): StringBuilder { + return this.append("$key: $value\n") +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 165c811904..6380e0f13b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -139,12 +139,12 @@ sealed class SendAction : SendScreenAction { data class SendError(override val error: TapError) : SendAction(), ErrorAction sealed class Dialog : SendAction() { - data class ShowTezosWarningDialog( + data class TezosWarningDialog( val reduceCallback: () -> Unit, val sendAllCallback: () -> Unit, val reduceAmount: BigDecimal, ) : Dialog() - + data class SendTransactionFails(val errorMessage: String): Dialog() object Hide : Dialog() } data class SetWarnings(val warningList: List) : SendAction() 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 2af762a25c..faad006fc5 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 @@ -21,6 +21,7 @@ import com.tangem.tap.features.send.redux.FeeAction.RequestFee import com.tangem.tap.features.send.redux.states.SendButtonState import com.tangem.tap.features.send.redux.states.TransactionExtrasState import com.tangem.tap.scope +import com.tangem.tap.store import com.tangem.tap.tangemSdk import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -50,7 +51,7 @@ val sendMiddleware: Middleware = { dispatch, appState -> } private fun verifyAndSendTransaction( - action: SendActionUi.SendAmountToRecipient, appState: AppState?, dispatch: (Action) -> Unit + action: SendActionUi.SendAmountToRecipient, appState: AppState?, dispatch: (Action) -> Unit, ) { val sendState = appState?.sendState ?: return val walletManager = appState.globalState.scanNoteResponse?.walletManager ?: return @@ -66,7 +67,7 @@ private fun verifyAndSendTransaction( when { hadTezosError -> { val reduceAmount = walletManager.wallet.blockchain.minimalAmount() - dispatch(SendAction.Dialog.ShowTezosWarningDialog(reduceCallback = { + dispatch(SendAction.Dialog.TezosWarningDialog(reduceCallback = { dispatch(AmountAction.SetAmount(typedAmount.value!!.minus(reduceAmount), false)) dispatch(AmountActionUi.CheckAmountToSend) }, sendAllCallback = { @@ -92,7 +93,7 @@ private fun sendTransaction( destinationAddress: String, transactionExtras: TransactionExtrasState, card: Card, - dispatch: (Action) -> Unit + dispatch: (Action) -> Unit, ) { dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS)) var txData = walletManager.createTransaction(amountToSend, feeAmount, destinationAddress) @@ -136,19 +137,23 @@ private fun sendTransaction( when { message == null -> { dispatch(SendAction.SendError(TapError.UnknownError)) + updateFeedbackManager(walletManager, amountToSend, feeAmount, destinationAddress, card) + dispatch(SendAction.Dialog.SendTransactionFails("unknown error")) } message.contains("50002") -> { // user was cancelled the operation by closing the Sdk bottom sheet } // make it easier latter by handling an appropriate enumError or, like on iOS, // accept a string identifier of the error message - message.contains("Target account is not created. To create account send 1+ XLM.")-> { + message.contains("Target account is not created. To create account send 1+ XLM.") -> { dispatch(SendAction.SendError(TapError.XmlError.AssetAccountNotCreated)) } else -> { Timber.e(throwable) FirebaseCrashlytics.getInstance().recordException(throwable) dispatch(SendAction.SendError(TapError.CustomError(message))) + updateFeedbackManager(walletManager, amountToSend, feeAmount, destinationAddress, card) + dispatch(SendAction.Dialog.SendTransactionFails(message)) } } } @@ -160,6 +165,29 @@ private fun sendTransaction( } } +private fun updateFeedbackManager( + walletManager: WalletManager, + amountToSend: Amount, + feeAmount: Amount, + destinationAddress: String, + card: Card, +) { + val infoHolder = store.state.globalState.feedbackManager?.infoHolder ?: return + val amountState = store.state.sendState.amountState + + infoHolder.cardId = card.cardId + infoHolder.blockchain = walletManager.wallet.blockchain + infoHolder.sourceAddress = walletManager.wallet.address + infoHolder.destinationAddress = destinationAddress + infoHolder.amount = amountToSend.value?.stripZeroPlainString() ?: "0" + infoHolder.fee = feeAmount.value?.stripZeroPlainString() ?: "0" + infoHolder.cardFirmwareVersion = card.firmwareVersion.version + if (amountState.typeOfAmount is AmountType.Token) { + infoHolder.token = amountState.amountToExtract?.currencySymbol ?: "" + } +// infoHolder.transactionHex = "" +} + fun extractErrorsForAmountField(errors: EnumSet): EnumSet { val showIntoAmountField = EnumSet.noneOf(TransactionError::class.java) errors.forEach { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 0d816d6d62..f042f8c1ae 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -42,7 +42,8 @@ private class SendReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState { val result = when (action) { is SendAction.ChangeSendButtonState -> sendState.copy(sendButtonState = action.state) - is SendAction.Dialog.ShowTezosWarningDialog -> sendState.copy(dialog = action) + is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action) + is SendAction.Dialog.SendTransactionFails -> sendState.copy(dialog = action) is SendAction.Dialog.Hide -> sendState.copy(dialog = null) is SendAction.SetWarnings -> sendState.copy(sendWarningsList = action.warningList) else -> return sendState diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt new file mode 100644 index 0000000000..cb65943200 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.features.send.ui.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.feedback.SendTransactionFailedEmail +import com.tangem.tap.features.send.redux.SendAction +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.store +import com.tangem.wallet.R + +/** +[REDACTED_AUTHOR] + */ +class SendTransactionFailsDialog { + + companion object { + fun create(context: Context, dialog: SendAction.Dialog.SendTransactionFails): AlertDialog { + return AlertDialog.Builder(context).apply { + setTitle(R.string.alert_failed_to_send_transaction_title) + setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, dialog.errorMessage)) + setNeutralButton(R.string.alert_button_send_feedback) { _, _ -> + store.dispatch(GlobalAction.SendFeedback(SendTransactionFailedEmail(dialog.errorMessage))) + } + setPositiveButton(R.string.common_no) { _, _ -> } + setOnDismissListener { store.dispatch(WalletAction.HideDialog) } + }.create() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/TezosWarningDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/TezosWarningDialog.kt index 45b092ea8c..afb6276cb7 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/TezosWarningDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/TezosWarningDialog.kt @@ -9,10 +9,10 @@ import com.tangem.wallet.R class TezosWarningDialog(context: Context) : AlertDialog(context) { companion object { - fun create(context: Context, showDialogData: SendAction.Dialog.ShowTezosWarningDialog): AlertDialog { + fun create(context: Context, showDialogData: SendAction.Dialog.TezosWarningDialog): AlertDialog { val reduceAmount = showDialogData.reduceAmount.toPlainString() return Builder(context).apply { - setTitle(context.getString(R.string.common_warning)) + setTitle(R.string.common_warning) setMessage(context.getString(R.string.xtz_withdrawal_message_warning, reduceAmount)) setNegativeButton(R.string.xtz_withdrawal_message_ignore) { _, _ -> showDialogData.sendAllCallback() diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 20d3988515..01eefabfd1 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -21,6 +21,7 @@ import com.tangem.tap.features.send.redux.reducers.ReceiptReducer import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.features.send.ui.FeeUiHelper import com.tangem.tap.features.send.ui.SendFragment +import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog import com.tangem.tap.features.wallet.ui.WarningMessagesAdapter import com.tangem.tap.store @@ -97,12 +98,18 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val sendFragment = (fg as? SendFragment) ?: return when (state.dialog) { - is SendAction.Dialog.ShowTezosWarningDialog -> { + is SendAction.Dialog.TezosWarningDialog -> { if (dialog == null) { dialog = TezosWarningDialog.create(fg.requireContext(), state.dialog) dialog?.show() } } + is SendAction.Dialog.SendTransactionFails -> { + if (dialog == null) { + dialog = SendTransactionFailsDialog.create(fg.requireContext(), state.dialog) + dialog?.show() + } + } else -> { dialog?.dismiss() dialog = null diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index 5f11e932bf..d7e4306e60 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.wallet.redux import android.content.Context +import com.tangem.TangemError import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.address.AddressType @@ -24,20 +25,28 @@ sealed class WalletAction : Action { } data class LoadWallet( - val wallet: Wallet, val artworkId: String?, val allowTopUp: Boolean + val wallet: Wallet, val artworkId: String?, val allowTopUp: Boolean, ) : WalletAction() { data class Success(val wallet: Wallet) : WalletAction() data class NoAccount(val amountToCreateAccount: String) : WalletAction() data class Failure(val errorMessage: String? = null) : WalletAction() } - object CheckIfWarningNeeded : WalletAction() object CheckHashesCountOnline : WalletAction() object NeedToCheckHashesCountOnline : WalletAction() object ConfirmHashesCount : WalletAction() - data class SetWarnings(val warningList: List) : WalletAction() object SaveCardId : WalletAction() + object Warnings : WalletAction() { + object CheckIfNeeded : WalletAction() + data class SetWarnings(val warningList: List) : WalletAction() + + object AppRating : WalletAction() { + object SetNeverToShow : WalletAction() + object RemindLater : WalletAction() + } + } + object UpdateWallet : WalletAction() { object ScheduleUpdatingWallet : WalletAction() data class Success(val wallet: Wallet) : WalletAction() @@ -63,6 +72,8 @@ sealed class WalletAction : Action { } object Scan : WalletAction() + class ScanCardFinished(val scanError: TangemError? = null) : WalletAction() + data class Send(val amount: Amount? = null) : WalletAction() { data class ChooseCurrency(val amounts: List?) : WalletAction() object Cancel : WalletAction() @@ -85,8 +96,12 @@ sealed class WalletAction : Action { } } - object ShowQrCode : WalletAction() + object ShowDialog : WalletAction() { + object QrCode : WalletAction() + object ScanFails : WalletAction() + } object HideDialog : WalletAction() + data class ExploreAddress(val context: Context) : WalletAction() object CreateWallet : WalletAction() object EmptyWallet : WalletAction() @@ -102,7 +117,7 @@ sealed class WalletAction : Action { object SetOnboardingShown : TwinsAction() data class SetTwinCard( val secondCardId: String, val number: TwinCardNumber, - val isCreatingTwinCardsAllowed: Boolean + val isCreatingTwinCardsAllowed: Boolean, ) : TwinsAction() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt index a06b52a6b1..7c8c4d1438 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt @@ -4,6 +4,7 @@ import android.content.Intent import android.net.Uri import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat +import com.tangem.TangemSdkError import com.tangem.blockchain.common.* import com.tangem.blockchain.extensions.SimpleResult import com.tangem.commands.common.card.Card @@ -136,6 +137,19 @@ class WalletMiddleware { store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) } } + store.dispatch(WalletAction.ScanCardFinished()) + } + is CompletionResult.Failure -> { + if (result.error !is TangemSdkError.UserCancelled) { + // Weird things... If you run the code below without coroutines, + // then rescanning will be impossible + scope.launch(Dispatchers.Main) { + store.dispatch(WalletAction.ScanCardFinished(result.error)) + if (store.state.walletState.scanCardFailsCounter >= 2) { + store.dispatch(WalletAction.ShowDialog.ScanFails) + } + } + } } } } @@ -173,7 +187,7 @@ class WalletMiddleware { store.dispatch(NavigationAction.NavigateTo(AppScreen.Send)) } } - is WalletAction.CheckIfWarningNeeded -> { + is WalletAction.Warnings.CheckIfNeeded -> { val globalState = store.state.globalState val validator = globalState.scanNoteResponse?.walletManager as? SignatureCountValidator globalState.scanNoteResponse?.card?.let { card -> @@ -184,7 +198,8 @@ class WalletMiddleware { } updateWarningMessages() } - + val readyToShow = preferencesStorage.appRatingLaunchObserver.isReadyToShow() + if (readyToShow) addWarningMessage(WarningMessagesManager.appRatingWarning(), true) } is WalletAction.CheckHashesCountOnline -> checkHashesCountOnline() is WalletAction.SaveCardId -> { @@ -198,6 +213,12 @@ class WalletMiddleware { is WalletAction.TwinsAction.SetOnboardingShown -> { preferencesStorage.saveTwinsOnboardingShown() } + is WalletAction.Warnings.AppRating.RemindLater -> { + preferencesStorage.appRatingLaunchObserver.applyDelayedShowing() + } + is WalletAction.Warnings.AppRating.SetNeverToShow -> { + preferencesStorage.appRatingLaunchObserver.setNeverToShow() + } } next(action) } @@ -290,7 +311,8 @@ class WalletMiddleware { private fun updateWarningMessages() { val warningManager = store.state.globalState.warningManager ?: return - store.dispatch(WalletAction.SetWarnings(warningManager.getWarnings(WarningMessage.Location.MainScreen))) + store.dispatch(WalletAction.Warnings.SetWarnings( + warningManager.getWarnings(WarningMessage.Location.MainScreen))) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt index 275ad57895..f458fa9433 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt @@ -176,7 +176,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { } newState = newState.copy(cardImage = Artwork(artworkId = artworkUrl)) } - is WalletAction.ShowQrCode -> { + is WalletAction.ShowDialog.QrCode -> { newState = newState.copy( walletDialog = WalletDialog.QrDialog( newState.walletAddresses?.selectedAddress?.shareUrl?.toQrCode(), @@ -185,6 +185,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) ) } + is WalletAction.ShowDialog.ScanFails -> { + newState = newState.copy(walletDialog = WalletDialog.ScanFailsDialog) + } is WalletAction.HideDialog -> { newState = newState.copy(walletDialog = null) } @@ -215,7 +218,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) } is WalletAction.Send.Cancel -> newState = newState.copy(walletDialog = null) - is WalletAction.SetWarnings -> newState = newState.copy(mainWarningsList = action.warningList) + is WalletAction.Warnings.SetWarnings -> newState = newState.copy(mainWarningsList = action.warningList) is WalletAction.NeedToCheckHashesCountOnline -> newState = newState.copy(hashesCountVerified = false) is WalletAction.ConfirmHashesCount -> @@ -254,6 +257,13 @@ private fun internalReduce(action: Action, state: AppState): WalletState { twinCardsState = newState.twinCardsState?.copy(showTwinOnboarding = false) ) } + is WalletAction.ScanCardFinished -> { + newState = if (action.scanError == null) { + newState.copy(scanCardFailsCounter = 0) + } else { + newState.copy(scanCardFailsCounter = newState.scanCardFailsCounter + 1) + } + } } return newState } @@ -288,7 +298,7 @@ private fun handleTopUpActions(action: WalletAction.TopUpAction, state: TopUpSta } private fun onWalletLoaded( - wallet: Wallet, walletState: WalletState, topUpAllowed: Boolean? = null + wallet: Wallet, walletState: WalletState, topUpAllowed: Boolean? = null, ): WalletState { val fiatCurrencySymbol = store.state.globalState.appCurrency val token = wallet.getFirstToken() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index 1b541930c7..1139758b62 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -28,7 +28,8 @@ data class WalletState( val mainButton: WalletMainButton = WalletMainButton.SendButton(false), val topUpState: TopUpState = TopUpState(), val twinCardsState: TwinCardsState? = null, - val mainWarningsList: List = mutableListOf() + val mainWarningsList: List = mutableListOf(), + val scanCardFailsCounter: Int = 0, ) : StateType { val showDetails: Boolean = currencyData.status != com.tangem.tap.features.wallet.ui.BalanceStatus.EmptyCard && @@ -52,6 +53,7 @@ sealed class WalletDialog { data class CreatePayIdDialog(val creatingPayIdState: CreatingPayIdState?) : WalletDialog() data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog() data class TwinsOnboardingFragment(val secondCardId: String): WalletDialog() + object ScanFailsDialog: WalletDialog() } enum class ProgressState { Loading, Done, Error } 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 749486e3fe..2fd33050f7 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 @@ -26,6 +26,7 @@ import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog import com.tangem.tap.features.wallet.ui.dialogs.PayIdDialog import com.tangem.tap.features.wallet.ui.dialogs.QrDialog +import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.card_balance.* @@ -208,7 +209,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber R.string.wallet_button_send @@ -301,6 +302,11 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber { + if (dialog == null) dialog = ScanFailsDialog.create(requireContext()).apply { + this.show() + } + } null -> { dialog?.dismiss() dialog = null diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WarningMessagesAdapter.kt index e7a2a559e3..aae70f9a67 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WarningMessagesAdapter.kt @@ -8,10 +8,14 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ItemDecoration +import com.google.android.play.core.review.ReviewManagerFactory import com.tangem.tap.common.extensions.getString +import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.configurable.warningMessage.WarningMessage +import com.tangem.tap.features.feedback.RateCanBeBetterEmail +import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.layout_warning.view.* @@ -40,7 +44,7 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) { fun bind(warning: WarningMessage) { setBgColor(warning.priority) setText(warning) - setupOkButton(warning) + setupControlButtons(warning) } private fun setText(warning: WarningMessage) { @@ -59,10 +63,47 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) { view.card_view.setCardBackgroundColor(view.context.resources.getColor(color)) } - private fun setupOkButton(warning: WarningMessage) { - view.btn_got_it.show(warning.type == WarningMessage.Type.Temporary) - view.btn_got_it.setOnClickListener { - store.dispatch(GlobalAction.HideWarningMessage(warning)) + private fun setupControlButtons(warning: WarningMessage) { + when (warning.type) { + WarningMessage.Type.Permanent -> { + view.group_controls_temporary.hide() + view.group_controls_rating.hide() + } + WarningMessage.Type.Temporary -> { + view.group_controls_rating.hide() + view.group_controls_temporary.show() + view.btn_got_it.setOnClickListener { store.dispatch(GlobalAction.HideWarningMessage(warning)) } + } + WarningMessage.Type.AppRating -> { + view.group_controls_temporary.hide() + view.group_controls_rating.show() + view.btn_close.setOnClickListener { + store.dispatch(GlobalAction.HideWarningMessage(warning)) + store.dispatch(WalletAction.Warnings.AppRating.RemindLater) + } + view.btn_can_be_better.setOnClickListener { + store.dispatch(GlobalAction.HideWarningMessage(warning)) + store.dispatch(GlobalAction.SendFeedback(RateCanBeBetterEmail())) + } + store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) + view.btn_really_cool.setOnClickListener { + val context = view.context + val reviewManager = ReviewManagerFactory.create(context) + val flow = reviewManager.requestReviewFlow() + flow.addOnCompleteListener { + if (it.isSuccessful) { +// val info = it.result +// Toast.makeText(context, "success", Toast.LENGTH_SHORT).show() + } else { +// Toast.makeText(context, "fail", Toast.LENGTH_SHORT).show() + } + }.addOnFailureListener { +// Toast.makeText(context, "failure", Toast.LENGTH_SHORT).show() + } + store.dispatch(GlobalAction.HideWarningMessage(warning)) + } + store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) + } } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt new file mode 100644 index 0000000000..9a8ff9ace5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.features.wallet.ui.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.features.feedback.ScanFailsEmail +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.store +import com.tangem.wallet.R + +/** +[REDACTED_AUTHOR] + */ +class ScanFailsDialog { + + companion object { + fun create(context: Context): AlertDialog { + return AlertDialog.Builder(context).apply { + setTitle(context.getString(R.string.common_warning)) + setMessage(R.string.alert_troubleshooting_scan_card_title) + setPositiveButton(R.string.alert_button_request_support) { _, _ -> + store.dispatch(GlobalAction.SendFeedback(ScanFailsEmail())) + } + setNegativeButton(R.string.common_cancel) { _, _ -> } + setOnDismissListener { store.dispatch(WalletAction.HideDialog) } + }.create() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt index 08346630f3..0c0179481e 100644 --- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt @@ -15,12 +15,13 @@ import com.tangem.tap.network.coinmarketcap.FiatCurrency class PreferencesStorage(applicationContext: Application) { - private val preferences: SharedPreferences by lazy { - applicationContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) - } + private val preferences: SharedPreferences = applicationContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + + val appRatingLaunchObserver: AppRatingLaunchObserver init { incrementLaunchCounter() + appRatingLaunchObserver = AppRatingLaunchObserver(preferences, getCountOfLaunches()) } private val fiatCurrenciesAdapter: JsonAdapter> by lazy { @@ -97,4 +98,37 @@ class PreferencesStorage(applicationContext: Application) { private const val APP_LAUNCH_COUNT_KEY = "launchCount" } +} + +class AppRatingLaunchObserver( + private val preferences: SharedPreferences, + private val launchCounts: Int, +) { + private val K_SHOW_RATING_DIALOG = "show_rating_dialog_after_app_launch_count" + + private val deferShowing = 20 + private val firstShowing = 3 + + fun isReadyToShow(): Boolean { + if (launchCounts == firstShowing) return true + + val nextShowingAt = getCounterOfNextShowing() + return launchCounts >= nextShowingAt + } + + fun applyDelayedShowing() { + if (getCounterOfNextShowing() < launchCounts) updateNextShowing(launchCounts + deferShowing) + } + + fun setNeverToShow() { + updateNextShowing(999999999) + } + + private fun updateNextShowing(at: Int) { + preferences.edit().putInt(K_SHOW_RATING_DIALOG, at).apply() + } + + private fun getCounterOfNextShowing(): Int { + return preferences.getInt(K_SHOW_RATING_DIALOG, firstShowing) + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 7a99598875..bea24a361d 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -170,6 +170,19 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_settings_title" /> + + + app:layout_constraintTop_toBottomOf="@id/tv_send_feedback" /> + + +