diff --git a/app/build.gradle b/app/build.gradle
index 2c09a36f9c..73344a3d04 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 {
@@ -69,8 +70,11 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.material:material:1.3.0'
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation "androidx.core:core-ktx:1.3.2"
+ 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.5'
+
// implementation 'com.tangem:blockchain:1.141.0'
implementation 'blockchain-sdk-kotlin:blockchain:tangem-20210302.084810-3'
diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml
index 4cc2b19474..bf9ef4d842 100644
--- a/app/src/debug/res/values/strings.xml
+++ b/app/src/debug/res/values/strings.xml
@@ -1,6 +1,6 @@
- DEBUG
+ DebugTangem
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2b7b764bfc..ee1acb1fe8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -81,6 +81,17 @@
+
+
+
+
+
\ No newline at end of file
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
diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt
index d5f031080b..0f609b7328 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
@@ -14,6 +15,9 @@ 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.domain.tokens.CurrenciesRepository
+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
@@ -49,10 +53,10 @@ class TapApplication : Application() {
PicassoHelper.initPicassoWithCaching(this)
currenciesRepository = CurrenciesRepository(this)
+ initFeedbackManager()
loadConfigs()
}
-
private fun loadConfigs() {
val moshi = createMoshi()
val localLoader = FeaturesLocalLoader(this, moshi)
@@ -62,4 +66,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/analytics/AnalyticsEvent.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
index 932897e204..4395bd0922 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEvent.kt
@@ -4,4 +4,8 @@ enum class AnalyticsEvent(val event: String) {
CARD_IS_SCANNED("card_is_scanned"),
TRANSACTION_IS_SENT("transaction_is_sent"),
READY_TO_SCAN("ready_to_scan"),
+ APP_RATING_DISPLAYED("rate_app_warning_displayed"),
+ APP_RATING_DISMISS("dismiss_rate_app_warning"),
+ APP_RATING_NEGATIVE("negative_rate_app_feedback"),
+ APP_RATING_POSITIVE("positive_rate_app_feedback"),
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
index 9dddeee4f2..1dc3586808 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt
@@ -4,5 +4,5 @@ import com.tangem.commands.common.card.Card
interface AnalyticsHandler {
- fun triggerEvent(event: AnalyticsEvent, card: Card?)
+ fun triggerEvent(event: AnalyticsEvent, card: Card? = null)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
index 0d49728dd6..d1d3ce7a38 100644
--- a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
+++ b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt
@@ -8,8 +8,7 @@ import com.tangem.commands.common.card.Card
object FirebaseAnalyticsHandler : AnalyticsHandler {
override fun triggerEvent(event: AnalyticsEvent, card: Card?) {
- Firebase.analytics
- .logEvent(event.event, setCardData(card))
+ Firebase.analytics.logEvent(event.event, setCardData(card))
}
fun logException(name: String, throwable: Throwable) {
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 49bc412a5d..45bc0c2707 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
sealed class GlobalAction : Action {
@@ -15,8 +17,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 16892e5475..baec3d76f4 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.CheckSignedHashes.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 fcd42c9395..8df35e1064 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
@@ -52,6 +52,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 4d96b02a3c..52340df26d 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
@@ -18,6 +19,7 @@ data class GlobalState(
val tangemService: TangemService = TangemService(),
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/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt
index 37f159fdb8..521ed462b4 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt
@@ -49,7 +49,7 @@ sealed class TapError(
}
sealed class TapSdkError(override val messageResId: Int?) : Throwable(), TangemError {
- final override val code: Int = 1
+ final override val code: Int = 50100
override var customMessage: String = code.toString()
object CardForDifferentApp : TapSdkError(R.string.alert_unsupported_card)
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 d62759f6f4..51883d2e9a 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt
@@ -85,20 +85,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.isSendingToPayIdEnabled)
- configManager?.turnOff(ConfigManager.isTopUpEnabled)
- } else if (data.walletManager?.wallet?.blockchain == Blockchain.Bitcoin
- || data.card.cardData?.blockchainName == Blockchain.Bitcoin.id) {
- configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled)
- configManager?.resetToDefault(ConfigManager.isTopUpEnabled)
- } else {
- configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled)
- configManager?.resetToDefault(ConfigManager.isTopUpEnabled)
- }
+ updateConfigManager(data)
+ updateFeedbackManager(data)
+
withContext(Dispatchers.Main) {
store.dispatch(WalletAction.ResetState)
store.dispatch(GlobalAction.SaveScanNoteResponse(data))
@@ -120,9 +110,38 @@ class TapWalletManager {
}
}
+ private fun updateConfigManager(data: ScanNoteResponse) {
+ val configManager = store.state.globalState.configManager
+ if (TapWorkarounds.isStart2Coin) {
+ configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled)
+ configManager?.turnOff(ConfigManager.isTopUpEnabled)
+ } else if (data.walletManager?.wallet?.blockchain == Blockchain.Bitcoin
+ || data.card.cardData?.blockchainName == Blockchain.Bitcoin.id) {
+ configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled)
+ configManager?.resetToDefault(ConfigManager.isTopUpEnabled)
+ } else {
+ 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.CheckSignedHashes.CheckIfWarningNeeded)
+ //TODO: I made it to WalletAction.CheckSignedHashes.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/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt
index 1e18167a52..d0cc8b64b2 100644
--- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt
+++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt
@@ -1,6 +1,7 @@
package com.tangem.tap.domain
import com.tangem.commands.common.card.Card
+import com.tangem.commands.common.card.masks.Product
import java.util.*
object TapWorkarounds {
@@ -12,5 +13,26 @@ object TapWorkarounds {
isStart2Coin = card.cardData?.issuerName?.toLowerCase(Locale.US) == START_2_COIN_ISSUER
}
- const val START_2_COIN_ISSUER = "start2coin"
+ fun Card.isExcluded(): Boolean {
+ val cardData = this.cardData ?: return false
+ val productMask = cardData.productMask
+ val excludedBatch = excludedBatches.contains(cardData.batchId)
+ val excludedIssuerName = excludedIssuers.contains(cardData.issuerName?.capitalize(Locale.US))
+ val excludedProductMask = (productMask != null && // product mask is on cards v2.30 and later
+ !productMask.contains(Product.Note) && !productMask.contains(Product.TwinCard))
+ return excludedBatch || excludedIssuerName || excludedProductMask
+
+ }
+
+ private const val START_2_COIN_ISSUER = "start2coin"
+
+ private val excludedBatches = listOf(
+ "0027",
+ "0030",
+ "0031",
+ ) // Tangem tags
+
+ private val excludedIssuers = listOf(
+ "TTM BANK"
+ )
}
\ No newline at end of file
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/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt
index 3ceeaadda6..5fd45114d2 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
@@ -19,6 +19,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.extensions.toHexString
import com.tangem.tap.domain.TapSdkError
import com.tangem.tap.domain.TapWorkarounds
+import com.tangem.tap.domain.TapWorkarounds.isExcluded
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.domain.twins.isTwinCard
import com.tangem.tap.store
@@ -123,23 +124,13 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable "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"
+ 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"
+ }
+ }
+
+ 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/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt
index 5bb753ff4c..f37ef1c696 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.CardTou
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 wallets: List,
val isCreatingTwinWalletAllowed: Boolean?,
+ 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 9c90579d69..ee44e36c9d 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
@@ -75,6 +75,7 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta
),
securityScreenState = SecurityScreenState(currentOption = securityOption),
createTwinWalletState = twinsState,
+ 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 f4d58c3000..5ef1033e2f 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
@@ -18,13 +19,14 @@ 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: Uri? = null,
) : StateType
data class CardInfo(
val cardId: String,
val issuer: String,
- val signedHashes: Int
+ val signedHashes: Int,
)
enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty }
@@ -33,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/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt
index d40bb904d7..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.wallets.getOrNull(0)
+ val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet
if (wallet == null) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning))
return
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 50de2e5535..895f379b09 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,6 @@
package com.tangem.tap.features.details.ui
+import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
@@ -7,6 +8,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 +16,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.*
@@ -79,6 +82,13 @@ 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/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
index 3e2bc8cdf0..fb46ed08da 100644
--- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt
@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
+import androidx.transition.TransitionInflater
import com.google.android.material.snackbar.Snackbar
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.store
@@ -30,6 +31,9 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) {
store.dispatch(NavigationAction.PopBackTo())
}
})
+ val inflater = TransitionInflater.from(requireContext())
+ enterTransition = inflater.inflateTransition(R.transition.slide_right)
+ exitTransition = inflater.inflateTransition(R.transition.fade)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
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 6888e3b462..8a87b6108f 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
@@ -141,12 +141,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/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt
index ded3984926..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,19 +1,17 @@
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
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)
@@ -110,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
@@ -131,6 +133,9 @@ internal class AddressPayIdMiddleware {
val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, supposedAddress)
if (failReason == null) {
+ noSchemeAddress.getQueryParameter("amount")?.toBigDecimalOrNull()?.let {
+ dispatch(AmountAction.SetAmount(it, false))
+ }
dispatch(SetWalletAddress(supposedAddress, isUserInput))
dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, null))
} else {
@@ -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
@@ -151,14 +156,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
@@ -186,4 +187,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/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt
index 47d30c72d5..115c1c2033 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
@@ -22,6 +22,7 @@ import com.tangem.tap.features.send.redux.states.SendButtonState
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.scope
+import com.tangem.tap.store
import com.tangem.tap.tangemSdk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -52,7 +53,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
@@ -68,7 +69,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 = {
@@ -94,7 +95,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)
@@ -144,6 +145,8 @@ 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
@@ -157,6 +160,8 @@ private fun sendTransaction(
Timber.e(throwable)
FirebaseCrashlytics.getInstance().recordException(throwable)
dispatch(SendAction.SendError(TapError.CustomError(message)))
+ updateFeedbackManager(walletManager, amountToSend, feeAmount, destinationAddress, card)
+ dispatch(SendAction.Dialog.SendTransactionFails(message))
}
}
}
@@ -168,6 +173,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 4144059510..527d3f6545 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/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt
index cba07cdeb5..6ee78a5baf 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
@@ -149,9 +149,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() {
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 8084b7e5d5..973ed08f8b 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.adapters.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
@@ -184,22 +191,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)
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 222d18a108..182e1d63d5 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
@@ -2,6 +2,9 @@ package com.tangem.tap.features.wallet.redux
import android.content.Context
import com.tangem.blockchain.common.*
+import com.tangem.TangemError
+import com.tangem.blockchain.common.Amount
+import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.address.AddressType
import com.tangem.commands.common.card.Card
import com.tangem.tap.common.redux.NotificationAction
@@ -30,7 +33,8 @@ sealed class WalletAction : Action {
data class SetArtworkId(val artworkId: String?) : WalletAction()
-// sealed class ProcessWallet : WalletAction() {
+
+ // sealed class ProcessWallet : WalletAction() {
// data class LoadWallet(val artworkId: String?, val allowTopUp: Boolean
// ) : ProcessWallet() {
// data class Success(val wallet: Wallet) : ProcessWallet()
@@ -45,7 +49,6 @@ sealed class WalletAction : Action {
// }
// }
-
sealed class MultiWallet : WalletAction() {
data class SetIsMultiwalletAllowed(val isMultiwalletAllowed: Boolean) : MultiWallet()
data class AddWalletManagers(val walletManagers: List) : MultiWallet()
@@ -68,7 +71,15 @@ sealed class WalletAction : Action {
object SaveCardId : CheckSignedHashes()
}
- data class SetWarnings(val warningList: List) : WalletAction()
+ object Warnings : WalletAction() {
+ object CheckIfNeeded : WalletAction()
+ data class SetWarnings(val warningList: List) : WalletAction()
+
+ object AppRating : WalletAction() {
+ object SetNeverToShow : WalletAction()
+ object RemindLater : WalletAction()
+ }
+ }
data class UpdateWallet(val currency: CryptoCurrencyName? = null) : WalletAction() {
object ScheduleUpdatingWallet : WalletAction()
@@ -88,40 +99,59 @@ sealed class WalletAction : Action {
object Failure : WalletAction()
}
-
- data class CopyAddress(val address: String, val context: Context) : WalletAction() {
- object Success : WalletAction(), NotificationAction {
- override val messageResource = R.string.wallet_notification_address_copied
- }
- }
-
- data class ShareAddress(val address: String, val context: Context) : WalletAction()
-
- object ShowQrCode : WalletAction()
- object HideDialog : WalletAction()
- data class ExploreAddress(val exploreUrl: String, val context: Context) : WalletAction()
-
- object CreateWallet : WalletAction()
- object EmptyWallet : WalletAction()
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()
}
- sealed class TopUpAction : WalletAction() {
- data class TopUp(val context: Context, val toolbarColor: Int) : TopUpAction()
- }
+ object CreatePayId : WalletAction() {
+ data class CompleteCreatingPayId(val payId: String) : WalletAction()
+ data class Success(val payId: String) : WalletAction()
+ object EmptyField : WalletAction(), ErrorAction {
+ override val error = TapError.PayIdEmptyField
+ }
- data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
+ data class CopyAddress(val address: String, val context: Context) : WalletAction() {
+ object Success : WalletAction(), NotificationAction {
+ override val messageResource = R.string.wallet_notification_address_copied
+ }
+ }
- sealed class TwinsAction : WalletAction() {
- object ShowOnboarding : TwinsAction()
- object SetOnboardingShown : TwinsAction()
- data class SetTwinCard(
- val secondCardId: String, val number: TwinCardNumber,
- val isCreatingTwinCardsAllowed: Boolean
- ) : TwinsAction()
- }
-}
\ No newline at end of file
+ data class ShareAddress(val address: String, val context: Context) : WalletAction()
+
+ object ShowDialog : WalletAction() {
+ object QrCode : WalletAction()
+ object ScanFails : WalletAction()
+ }
+
+ object HideDialog : WalletAction()
+
+ data class ExploreAddress(val exploreUrl: String, val context: Context) : WalletAction()
+
+ object CreateWallet : WalletAction()
+ object EmptyWallet : WalletAction()
+ object Scan : WalletAction()
+
+ data class Send(val amount: Amount? = null) : WalletAction() {
+ data class ChooseCurrency(val amounts: List?) : WalletAction()
+ object Cancel : WalletAction()
+ }
+
+ sealed class TopUpAction : WalletAction() {
+ data class TopUp(val context: Context, val toolbarColor: Int) : TopUpAction()
+ }
+
+ data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
+
+ sealed class TwinsAction : WalletAction() {
+ object ShowOnboarding : TwinsAction()
+ object SetOnboardingShown : TwinsAction()
+ data class SetTwinCard(
+ val secondCardId: String, val number: TwinCardNumber,
+ 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
new file mode 100644
index 0000000000..3f3534891e
--- /dev/null
+++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt
@@ -0,0 +1,356 @@
+package com.tangem.tap.features.wallet.redux
+
+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
+import com.tangem.commands.common.card.CardType
+import com.tangem.commands.common.network.Result
+import com.tangem.common.CompletionResult
+import com.tangem.common.extensions.getType
+import com.tangem.common.extensions.toHexString
+import com.tangem.tap.common.analytics.AnalyticsEvent
+import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
+import com.tangem.tap.common.extensions.copyToClipboard
+import com.tangem.tap.common.extensions.isGreaterThan
+import com.tangem.tap.common.redux.AppState
+import com.tangem.tap.common.redux.navigation.AppScreen
+import com.tangem.tap.common.redux.navigation.NavigationAction
+import com.tangem.tap.domain.PayIdManager
+import com.tangem.tap.domain.TapError
+import com.tangem.tap.domain.TopUpHelper
+import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
+import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
+import com.tangem.tap.domain.extensions.toSendableAmounts
+import com.tangem.tap.domain.twins.TwinsHelper
+import com.tangem.tap.domain.twins.isTwinCard
+import com.tangem.tap.features.details.redux.DetailsAction
+import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
+import com.tangem.tap.features.send.redux.PrepareSendScreen
+import com.tangem.tap.features.wallet.models.toPendingTransactions
+import com.tangem.tap.network.NetworkConnectivity
+import com.tangem.tap.network.NetworkStateChanged
+import com.tangem.tap.preferencesStorage
+import com.tangem.tap.scope
+import com.tangem.tap.store
+import com.tangem.tap.tangemSdkManager
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.rekotlin.Action
+import org.rekotlin.Middleware
+import java.math.BigDecimal
+
+class WalletMiddleware {
+ private val topUpMiddleware = TopUpMiddleware()
+
+ val walletMiddleware: Middleware = { dispatch, state ->
+ { next ->
+ { action ->
+ when (action) {
+ is WalletAction.TopUpAction -> topUpMiddleware.handle(action)
+ is WalletAction.LoadWallet -> {
+ scope.launch {
+ store.state.globalState.tapWalletManager.loadWalletData()
+ }
+ }
+ is WalletAction.LoadPayId -> {
+ scope.launch {
+ store.state.globalState.tapWalletManager.loadPayId()
+ }
+ }
+ is WalletAction.LoadFiatRate -> {
+ scope.launch {
+ store.state.globalState.tapWalletManager.loadFiatRate(store.state.globalState.appCurrency)
+ }
+ }
+ is WalletAction.CreateWallet -> {
+ if (store.state.walletState.twinCardsState != null) {
+ store.dispatch(DetailsAction.CreateTwinWalletAction.ShowWarning(
+ store.state.globalState.scanNoteResponse?.card?.cardId?.let {
+ TwinsHelper.getTwinCardNumber(it)
+ },
+ CreateTwinWallet.CreateWallet
+ ))
+ } else {
+ scope.launch {
+ val result = tangemSdkManager.createWallet(
+ store.state.globalState.scanNoteResponse?.card?.cardId
+ )
+ when (result) {
+ is CompletionResult.Success -> {
+ store.state.globalState.tapWalletManager
+ .onCardScanned(result.data)
+ }
+
+ }
+ }
+ }
+ }
+ is WalletAction.UpdateWallet -> {
+ if (store.state.walletState.state == ProgressState.Done) {
+ scope.launch { store.state.globalState.tapWalletManager.updateWallet() }
+ }
+ }
+ is WalletAction.UpdateWallet.Success -> setupWalletUpdate(action.wallet)
+ is WalletAction.LoadWallet.Success -> {
+ store.dispatch(WalletAction.CheckHashesCountOnline)
+ if (!store.state.walletState.updatingWallet) setupWalletUpdate(action.wallet)
+ tryToShowAppRatingWarning(action.wallet)
+ }
+ is WalletAction.CreatePayId.CompleteCreatingPayId -> {
+ scope.launch {
+ val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
+ val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet
+ val publicKey = store.state.globalState.scanNoteResponse?.card?.cardPublicKey
+ if (cardId != null && wallet != null && publicKey != null) {
+ val result = PayIdManager().setPayId(
+ cardId, publicKey.toHexString(),
+ action.payId, wallet.address, wallet.blockchain
+ )
+ withContext(Dispatchers.Main) {
+ when (result) {
+ is Result.Success ->
+ store.dispatch(WalletAction.CreatePayId.Success(action.payId))
+ is Result.Failure -> {
+ val error = result.error as? TapError
+ ?: TapError.PayIdCreatingError
+ store.dispatch(WalletAction.CreatePayId.Failure(error))
+ }
+ }
+ }
+ }
+ }
+ }
+ is WalletAction.Scan -> {
+ scope.launch {
+ val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler)
+ when (result) {
+ is CompletionResult.Success -> {
+ tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card)
+ store.state.globalState.tapWalletManager
+ .onCardScanned(result.data, true)
+ if (store.state.walletState.twinCardsState != null) {
+ val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
+ if (showOnboarding) {
+ 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)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ is WalletAction.LoadData -> {
+ scope.launch {
+ store.state.globalState.scanNoteResponse?.let {
+ store.state.globalState.tapWalletManager.loadData(it)
+ }
+ }
+ }
+ is NetworkStateChanged -> {
+ store.state.globalState.scanNoteResponse?.let { scanNoteResponse ->
+ store.dispatch(WalletAction.CheckHashesCountOnline)
+ scope.launch {
+ store.state.globalState.tapWalletManager.onCardScanned(scanNoteResponse)
+ }
+ }
+ }
+ is WalletAction.CopyAddress -> {
+ store.state.walletState.walletAddresses?.selectedAddress?.address?.let {
+ action.context.copyToClipboard(it)
+ store.dispatch(WalletAction.CopyAddress.Success)
+ }
+ }
+ is WalletAction.ExploreAddress -> {
+ val uri = Uri.parse(store.state.walletState.walletAddresses?.selectedAddress?.exploreUrl)
+ val intent = Intent(Intent.ACTION_VIEW, uri)
+ ContextCompat.startActivity(action.context, intent, null)
+ }
+ is WalletAction.Send -> {
+ val newAction = prepareSendAction(action.amount)
+ store.dispatch(newAction)
+ if (newAction is PrepareSendScreen) {
+ store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
+ }
+ }
+ is WalletAction.Warnings.CheckIfNeeded -> {
+ val globalState = store.state.globalState
+ val validator = globalState.scanNoteResponse?.walletManager as? SignatureCountValidator
+ globalState.scanNoteResponse?.card?.let { card ->
+ store.state.globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local)
+ if (card.getType() != CardType.Release) addWarningMessage(WarningMessagesManager.devCardWarning())
+ if (!preferencesStorage.wasCardScannedBefore(card.cardId)) {
+ checkIfWarningNeeded(card, validator)?.let { addWarningMessage(it) }
+ }
+ updateWarningMessages()
+ }
+ }
+ is WalletAction.CheckHashesCountOnline -> checkHashesCountOnline()
+ is WalletAction.SaveCardId -> {
+ val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
+ cardId?.let { preferencesStorage.saveScannedCardId(it) }
+ }
+ is WalletAction.TwinsAction.SetTwinCard -> {
+ val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
+ if (showOnboarding) store.dispatch(WalletAction.TwinsAction.ShowOnboarding)
+ }
+ is WalletAction.TwinsAction.SetOnboardingShown -> {
+ preferencesStorage.saveTwinsOnboardingShown()
+ }
+ is WalletAction.Warnings.AppRating.RemindLater -> {
+ preferencesStorage.appRatingLaunchObserver.applyDelayedShowing()
+ }
+ is WalletAction.Warnings.AppRating.SetNeverToShow -> {
+ preferencesStorage.appRatingLaunchObserver.setNeverToShow()
+ }
+ }
+ next(action)
+ }
+ }
+ }
+
+ private fun tryToShowAppRatingWarning(wallet: Wallet) {
+ val nonZeroWalletsCount = wallet.amounts.filter {
+ it.value.value?.isGreaterThan(BigDecimal.ZERO) ?: false
+ }.size
+ if (nonZeroWalletsCount > 0) {
+ preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds()
+ }
+ if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) {
+ FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED)
+ addWarningMessage(WarningMessagesManager.appRatingWarning(), true)
+ }
+ }
+
+ private fun setupWalletUpdate(wallet: Wallet) {
+ if (!wallet.recentTransactions.toPendingTransactions(wallet.address).isNullOrEmpty()) {
+ store.dispatch(WalletAction.UpdateWallet.ScheduleUpdatingWallet)
+ scope.launch(Dispatchers.IO) {
+ delay(10000)
+ withContext(Dispatchers.Main) {
+ store.dispatch(WalletAction.UpdateWallet)
+ }
+ }
+ }
+ }
+
+
+ private fun prepareSendAction(amount: Amount?): Action {
+ return if (amount != null) {
+ if (amount.type is AmountType.Token) {
+ PrepareSendScreen(store.state.walletState.wallet?.amounts?.get(AmountType.Coin), amount)
+ } else {
+ PrepareSendScreen(amount)
+ }
+ } else {
+ val amounts = store.state.walletState.wallet?.amounts?.toSendableAmounts()
+ if (amounts?.size ?: 0 > 1) {
+ WalletAction.Send.ChooseCurrency(amounts)
+ } else {
+ val amountToSend = amounts?.first()
+ PrepareSendScreen(amountToSend)
+ }
+ }
+ }
+
+ private fun checkIfWarningNeeded(
+ card: Card, signatureCountValidator: SignatureCountValidator? = null,
+ ): WarningMessage? {
+ if (card.isTwinCard()) return null
+
+ return if (signatureCountValidator == null) {
+ if (card.walletSignedHashes ?: 0 > 0) {
+ WarningMessagesManager.alreadySignedHashesWarning()
+ } else {
+ store.dispatch(WalletAction.SaveCardId)
+ null
+ }
+ } else {
+ store.dispatch(WalletAction.NeedToCheckHashesCountOnline)
+ null
+ }
+ }
+
+ private fun checkHashesCountOnline() {
+ if (store.state.walletState.hashesCountVerified != false) return
+ if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return
+
+ val card = store.state.globalState.scanNoteResponse?.card
+ if (card == null || preferencesStorage.wasCardScannedBefore(card.cardId)) return
+
+ if (card.isTwinCard()) return
+
+ val validator = store.state.globalState.scanNoteResponse?.walletManager
+ as? SignatureCountValidator
+ scope.launch {
+ val result = validator?.validateSignatureCount(card.walletSignedHashes ?: 0)
+ withContext(Dispatchers.Main) {
+ when (result) {
+ SimpleResult.Success -> {
+ store.dispatch(WalletAction.ConfirmHashesCount)
+ store.dispatch(WalletAction.SaveCardId)
+ }
+ is SimpleResult.Failure ->
+ if (result.error is BlockchainSdkError.SignatureCountNotMatched) {
+ addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
+ } else if (card.walletSignedHashes ?: 0 > 0) {
+ addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
+ }
+ }
+ }
+ }
+ }
+
+ private fun addWarningMessage(warning: WarningMessage, autoUpdate: Boolean = false) {
+ store.state.globalState.warningManager?.addWarning(warning)
+ if (autoUpdate) updateWarningMessages()
+ }
+
+ private fun updateWarningMessages() {
+ val warningManager = store.state.globalState.warningManager ?: return
+ store.dispatch(WalletAction.Warnings.SetWarnings(
+ warningManager.getWarnings(WarningMessage.Location.MainScreen)))
+ }
+}
+
+private class TopUpMiddleware {
+ fun handle(action: WalletAction.TopUpAction) {
+ when (action) {
+ is WalletAction.TopUpAction.TopUp -> {
+ val config = store.state.globalState.configManager?.config ?: return
+ val addresses = store.state.walletState.walletAddresses ?: return
+ if (addresses.list.isEmpty()) return
+
+ val defaultAddress = addresses.list[0].address
+ val url = TopUpHelper.getUrl(
+ store.state.walletState.currencyData.currencySymbol!!,
+ defaultAddress,
+ config.moonPayApiKey,
+ config.moonPayApiSecretKey
+ )
+ val customTabsIntent = CustomTabsIntent.Builder()
+ .setToolbarColor(action.toolbarColor)
+ .build()
+ customTabsIntent.launchUrl(action.context, Uri.parse(url));
+ }
+ }
+ }
+}
\ No newline at end of file
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 a75c65a889..282d78393a 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
@@ -213,7 +213,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
}
newState = newState.copy(cardImage = Artwork(artworkId = artworkUrl))
}
- is WalletAction.ShowQrCode -> {
+ is WalletAction.ShowDialog.QrCode -> {
val selectedWalletData = newState.getWalletData(newState.selectedWallet)
newState = newState.copy(
walletDialog = WalletDialog.QrDialog(
@@ -223,6 +223,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)
}
@@ -232,7 +235,10 @@ 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.TopUpAction -> {
+ newState = newState.copy(topUpState = handleTopUpActions(action, newState.topUpState))
+ }
is WalletAction.TopUpAction -> return newState
is WalletAction.ChangeSelectedAddress -> {
val selectedWalletData = newState.getWalletData(newState.selectedWallet)
@@ -246,6 +252,13 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
)
newState = newState.copy(wallets = wallets)
}
+ is WalletAction.ScanCardFinished -> {
+ newState = if (action.scanError == null) {
+ newState.copy(scanCardFailsCounter = 0)
+ } else {
+ newState.copy(scanCardFailsCounter = newState.scanCardFailsCounter + 1)
+ }
+ }
}
return newState
}
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 2958717c4a..6bf6a41ffc 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
@@ -25,6 +25,7 @@ data class WalletState(
val walletDialog: WalletDialog? = null,
val twinCardsState: TwinCardsState? = null,
val mainWarningsList: List = mutableListOf(),
+ val scanCardFailsCounter: Int = 0,
val wallets: List = emptyList(),
val walletManagers: List = emptyList(),
val isMultiwalletAllowed: Boolean = false,
@@ -101,6 +102,7 @@ sealed class WalletDialog {
) : WalletDialog()
data class SelectAmountToSendDialog(val amounts: List?) : 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 b7582f4972..4e87055b3d 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
@@ -19,6 +19,8 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
+import com.tangem.tap.domain.termsOfUse.CardTou
+import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.ui.adapters.SpacesItemDecoration
@@ -26,6 +28,10 @@ import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.tap.features.wallet.ui.wallet.MultiWalletView
import com.tangem.tap.features.wallet.ui.wallet.SingleWalletView
import com.tangem.tap.features.wallet.ui.wallet.WalletView
+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.fragment_wallet.*
@@ -160,6 +166,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber R.id.chip_legacy
+// is BitcoinAddressType.Segwit -> R.id.chip_default
+// is CardanoAddressType.Byron -> R.id.chip_legacy
+// is CardanoAddressType.Shelley -> R.id.chip_default
+// else -> View.NO_ID
+// }
+// }
+//
+// fun idToType(id: Int, blockchain: Blockchain?): AddressType? {
+// return when (id) {
+// R.id.chip_default -> {
+// when (blockchain) {
+// Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> BitcoinAddressType.Segwit
+// Blockchain.CardanoShelley -> CardanoAddressType.Shelley
+// else -> null
+// }
+// }
+// R.id.chip_legacy -> {
+// when (blockchain) {
+// Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> BitcoinAddressType.Legacy
+// Blockchain.CardanoShelley -> CardanoAddressType.Byron
+// else -> null
+// }
+// }
+// else -> null
+// }
+// }
+// }
+//}
+
+//TODO: handle scan failed dialog:
+//private fun handleDialogs(walletDialog: WalletDialog?) {
+// when (walletDialog) {
+// is WalletDialog.QrDialog -> {
+// if (walletDialog.qrCode != null && walletDialog.shareUrl != null) {
+// if (dialog == null) dialog = QrDialog(requireContext()).apply {
+// this.showQr(
+// walletDialog.qrCode, walletDialog.shareUrl, walletDialog.currencyName
+// )
+// }
+// }
+// }
+// is WalletDialog.CreatePayIdDialog -> {
+// when (walletDialog.creatingPayIdState) {
+// CreatingPayIdState.EnterPayId -> {
+// if (dialog == null) dialog = PayIdDialog(requireContext()).apply {
+// this.show()
+// }
+// (dialog as? PayIdDialog)?.stopProgress()
+// }
+// CreatingPayIdState.Waiting -> (dialog as? PayIdDialog)?.showProgress()
+// }
+// }
+// is WalletDialog.SelectAmountToSendDialog -> {
+// if (dialog == null) dialog = AmountToSendDialog(requireContext()).apply {
+// this.show(walletDialog.amounts)
+// }
+// }
+// is WalletDialog.ScanFailsDialog -> {
+// if (dialog == null) dialog = ScanFailsDialog.create(requireContext()).apply {
+// this.show()
+// }
+// }
+// null -> {
+// dialog?.dismiss()
+// dialog = null
+// }
+// }
+//}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt
index 3200ac34ec..cc744be634 100644
--- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt
+++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt
@@ -8,10 +8,16 @@ 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.analytics.AnalyticsEvent
+import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
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 +46,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 +65,50 @@ 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 {
+ FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_DISMISS)
+ store.dispatch(GlobalAction.HideWarningMessage(warning))
+ store.dispatch(WalletAction.Warnings.AppRating.RemindLater)
+ }
+ view.btn_can_be_better.setOnClickListener {
+ FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_NEGATIVE)
+ store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
+ store.dispatch(GlobalAction.HideWarningMessage(warning))
+ store.dispatch(GlobalAction.SendFeedback(RateCanBeBetterEmail()))
+ }
+ view.btn_really_cool.setOnClickListener {
+ FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE)
+ store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
+ 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))
+ }
+ }
}
}
}
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..9d45dd8c36 100644
--- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt
+++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt
@@ -11,16 +11,18 @@ import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.network.coinmarketcap.FiatCurrency
+import java.util.*
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 +99,60 @@ 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_AT_LAUNCH_COUNT = "showRatingDialogAtLaunchCount"
+ private val K_FUNDS_FOUND_DATE = "fundsFoundDate"
+ private val K_USER_WAS_INTERACT_WITH_RATING = "userWasInteractWithRating"
+
+ private val deferShowing = 20
+ private val firstShowing = 3
+ private var fundsFoundDate: Calendar? = null
+
+ init {
+ val dateTimeMs = preferences.getLong(K_FUNDS_FOUND_DATE, -1)
+ if (dateTimeMs > 0) fundsFoundDate = Calendar.getInstance().apply { timeInMillis = dateTimeMs }
+ }
+
+ fun foundWalletWithFunds() {
+ if (fundsFoundDate != null) return
+
+ fundsFoundDate = Calendar.getInstance()
+ preferences.edit().putLong(K_FUNDS_FOUND_DATE, fundsFoundDate!!.timeInMillis).apply()
+ }
+
+ fun isReadyToShow(): Boolean {
+ val fundsDate = fundsFoundDate ?: return false
+
+ if (!userWasInteractWithRating()) {
+ val diff = Calendar.getInstance().timeInMillis - fundsDate.timeInMillis
+ val diffInDays = diff / (100 * 60 * 60 * 24)
+ if (diffInDays >= firstShowing) return true
+ }
+
+ val nextShowing = getCounterOfNextShowing()
+ return launchCounts >= nextShowing
+ }
+
+ fun applyDelayedShowing() {
+ updateNextShowing(launchCounts + deferShowing)
+ }
+
+ fun setNeverToShow() {
+ updateNextShowing(999999999)
+ }
+
+ private fun updateNextShowing(at: Int) {
+ val editor = preferences.edit()
+ editor.putInt(K_SHOW_RATING_AT_LAUNCH_COUNT, at)
+ editor.putBoolean(K_USER_WAS_INTERACT_WITH_RATING, true)
+ editor.apply()
+ }
+
+ private fun userWasInteractWithRating(): Boolean = preferences.getBoolean(K_USER_WAS_INTERACT_WITH_RATING, false)
+ private fun getCounterOfNextShowing(): Int = preferences.getInt(K_SHOW_RATING_AT_LAUNCH_COUNT, 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 8fe7ff059f..b45e3e92ce 100644
--- a/app/src/main/res/layout/fragment_details.xml
+++ b/app/src/main/res/layout/fragment_details.xml
@@ -126,6 +126,21 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_signed_hashes_title" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/tv_card_tou" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/tv_send_feedback" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 4ae7d12378..0000000000
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
old mode 100644
new mode 100755
index f26b7cd005..e307de6027
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
deleted file mode 100644
index cd4844d78b..0000000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
deleted file mode 100644
index ad4b07ff80..0000000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index bb2457e1ff..0000000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
old mode 100644
new mode 100755
index d7b8e985cc..cfab76de31
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
deleted file mode 100644
index 115308a4f9..0000000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index 7fccb8a69b..0000000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 2a7e2a1395..0000000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
old mode 100644
new mode 100755
index 4f205d64ab..bddcb01270
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
deleted file mode 100644
index a6129e9f51..0000000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 3c3b30ca7e..0000000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index dc8fb9c530..0000000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
old mode 100644
new mode 100755
index ffebc72291..ac5cb152bb
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
deleted file mode 100644
index c018380e65..0000000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 97a83eb92a..0000000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 9d876c01e3..0000000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
old mode 100644
new mode 100755
index 613405d26a..533177434c
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
index dd3b3a6d67..d32c73f660 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
index fbe488e03a..9b72259299 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 405be1cf56..0000000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 521a7b537d..97c8b2d0af 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -12,7 +12,7 @@
Sie haben keinen Zugang zur Kamera erteilt, bitte passen Sie Ihre Datenschutzeinstellungen an
Einstellungen
Willkommen bei Tangem. Haben Sie eine dieser Karten?
- Willkommen bei Tangem Tap. Scannen Sie Ihre Karten um zu starten.
+ Willkommen bei Tangem. Scannen Sie Ihre Karten um zu starten.
Ja! Bitte scannen
Einkaufen
Halten Sie die \ncard hinter dem \nphone bereit und scannen Sie sie.
@@ -21,7 +21,7 @@
Scannen
Absenden
ein Wallet erstellen
- Tangem Tap
+ Tangem
eine Walletoption wählen
Token
Tokens
@@ -98,7 +98,7 @@
Diese Karte wurde früher bereits aufgeladen und Transaktionen wurden damit signiert. Ziehen Sie eine sofortige Auszahlung aller Beträge in Betracht, wenn Sie diese Karte von einer nicht vertrauenswürdigen Quelle erhalten haben.
Warnung
- Diese Karte ist für die Zusammenarbeit mit Tangem Tap nicht geeignet
+ Diese Karte ist für die Zusammenarbeit mit Tangem nicht geeignet
Die von Ihnen gescannte Karte ist eine Entwicklungskarte. Akzeptieren Sie sie nicht als Zahlungsmittel
Die vor September 2019 ausgestellten Karten können mit iPhone momentan nicht extrahiert werden. Wir arbeiten eng mit Apple zusammen, damit es in den nächsten Versionen von iOS möglich wäre.
Tippen um zu signieren
@@ -112,7 +112,7 @@
RECHTLICHER HAFTUNGSAUSSCHLUSS
-\n\n1.Tangem Tap App (Software)
+\n\n1.Tangem App (Software)
\n\nDie Software ist nur für die Verwendung mit Tangem hardware Wallets (Karten) über NFC-Schnittstelle geeignet. Die Software:
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index d48558a922..29e248d8d7 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -13,7 +13,7 @@
Vous n\'avez pas octroyé l’accès à votre caméra, veuillez modifier vos paramètres de confidentialité
Paramètres
Bienvenue à Tangem. Avez-vous une de nos cartes
- Encore une fois, bienvenue dans Tangem Tap. \nScannez votre carte pour commencer.
+ Encore une fois, bienvenue dans Tangem. \nScannez votre carte pour commencer.
Oui! Scannez-la.
Magasin
Préparez-vous à appuyer votre carte au dos de votre téléphone.
@@ -22,7 +22,7 @@
Scanner
Envoyer
Créer un portefeuille
- Tangem Tap
+ Tangem
Choisir une variante de portefeuille
Jeton d\'authentification
Jetons d\'authentification
@@ -98,7 +98,7 @@
Vous pouvez rencontrer des problèmes NFC avec certains iPhone 7/7 + lors de l’extraction
Cette carte a déjà été rechargée et a signé des transactions avant. Envisagez la possibilité de retirer tous les fonds immédiatement si vous avez reçu cette carte d\'une source non fiable.
Attention
- Cette carte n\'est pas conçue pour fonctionner avec Tangem Tap
+ Cette carte n\'est pas conçue pour fonctionner avec Tangem
La carte que vous avez scannée est une carte de développement. Ne l\'acceptez pas comme paiement
Les cartes Tangem émises avant septembre 2019 ne peuvent actuellement pas être extraites à l\'aide de l\'iPhone. Nous travaillons activement avec Apple pour rendre cela possible dans les futures versions d\'iOS.
Touchez pour signer
@@ -113,7 +113,7 @@
Avertissement
-\n\n1.Application Tangem Tap (Logiciel)
+\n\n1.Application Tangem (Logiciel)
\n\nLe logiciel est destiné à l\’utilisation exclusivement avec les e-portefeuilles Tangem (Cartes) à l\’aide de l\’interface NFC. Le logiciel NE réalise pas ce qui suit :
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index d33b5332db..a7011d4bcc 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -11,7 +11,7 @@
Non hai fornito l\'accesso alla tua videocamera, modifica le tue impostazioni sulla privacy
Impostazioni
Benvenuti in Tangem. Hai una delle nostre schede?>
- Benevenuti di nuovo su Tangem Tap. Scansiona la tua carta per iniziare.
+ Benevenuti di nuovo su Tangem. Scansiona la tua carta per iniziare.
Si! Scansiona carta.
Shop
Preparati ad avvicinare la tua carta sul retro del telefono.
@@ -20,7 +20,7 @@
Scansiona
Invia
Crea portafoglio
- Tangem Tap
+ Tangem
Scegli l\'opzione del portafoglio
Token
Token
@@ -95,7 +95,7 @@
Potresti riscontrare problemi con l\'NFC su alcuni iPhone 7/7 + durante la rimozione
Questa carta è già stata ricaricata e ha firmato transazioni in passato. Valuta la possibilità di prelevare immediatamente tutti i fondi se hai ricevuto questa carta da una fonte inaffidabile.
Attenzione
- Questa carta non è progettata per funzionare con Tangem Tap
+ Questa carta non è progettata per funzionare con Tangem
La carta che hai scansionato è una carta di sviluppo. Non utilizzarla come strumento di pagamento
Le schede emesse prima di settembre 2019 non possono attualmente essere utilizzate con un iPhone. Stiamo lavorando a stretto contatto con Apple per renderle disponibili nelle future versioni di iOS.
Avvicina per firmare
@@ -110,7 +110,7 @@
NOTA LEGALE\n
\n
-App Tangem Tap (software)\n
+App Tangem (software)\n
\n
1. Il software può essere utilizzato solo con i portafogli hardware Tangem (schede) tramite l\'interfaccia NFC. Il software NON esegue le seguenti operazioni:\n
\n
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b511669041..041c1aa018 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,6 +1,6 @@
- Tangem Tap
+ Tangem
No
Save changes
@@ -16,7 +16,7 @@
Settings
Welcome to Tangem.\nDo you have one of our cards?
- Welcome back to Tangem Tap. \nScan your card to start.
+ Welcome back to Tangem. \nScan your card to start.
Shop
Get ready to tap your \ncard to the back of the \nphone.
Tap in
@@ -26,7 +26,7 @@
Scan
Send
Create wallet
- Tangem Tap
+ Tangem
Choice wallet option
Token
@@ -109,7 +109,7 @@
You may experience NFC problems with some iPhone 7/7+ during the extraction
This card has been already topped up and signed transactions in the past. Consider immediate withdrawal of all funds if you have received this card from an untrusted source.
Warning
- This card it is not designed to work with Tangem Tap
+ This card it is not designed to work with Tangem
The card you scanned is a development card. Don’t accept it as a payment
Tangem cards manufactured before September 2019 cannot currently be extracted with an iPhone. We’re working hard with Apple to make it possible in future versions of iOS.
@@ -124,7 +124,7 @@
Legal Disclaimer
\n
- \n1. Tangem Tap application (Software)
+ \n1. Tangem application (Software)
\n
\nThe Software is intended for usage only with Tangem hardware wallets (Cards) via NFC interface. The Software DOES NOT:
\n
diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml
index b6a2be9cc6..d7fe68d48a 100644
--- a/app/src/main/res/values/strings_untranslated.xml
+++ b/app/src/main/res/values/strings_untranslated.xml
@@ -27,6 +27,7 @@ this wallet.
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.
+ Card terms of use
The twin address was successfully created
@@ -43,6 +44,25 @@ this wallet.
Invalid destination tag. It won\'t be added to the transaction
Invalid Memo ID. It won\'t be added to the transaction
+ App
+ Send feedback
+ Sent successfully
+ Thank you for your feedback
+ Your suggestions were sent
+ Thank your for your feedback. We will response as soon as possible
+ Failed to send email
+ Reason: %s
+ Can’t send a transaction
+ Reason: %s. Do you want to send feedback?
+ Are you having difficulty scanning your card?
+ Please try to tap the card exactly as shown in the animation or request support.
+ Try again
+ Request support
+ Send feedback
+ Really cool!
+ Can be better
+ One question
+ How do you like Tangem?
All fields are required
@@ -70,7 +90,7 @@ this wallet.
//Details
Blockchain
Manage tokens
-
+
Blockchains
Ethereum Tokens
diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml
new file mode 100644
index 0000000000..1a33b59980
--- /dev/null
+++ b/app/src/main/res/xml/provider_paths.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file