diff --git a/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt new file mode 100644 index 0000000000..c5d9d91396 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/termsOfUse/CardTou.kt @@ -0,0 +1,46 @@ +package com.tangem.tap.domain.termsOfUse + +import android.content.res.Resources +import android.net.Uri +import androidx.core.os.ConfigurationCompat +import com.tangem.commands.common.card.Card +import java.util.* + +/** +[REDACTED_AUTHOR] + */ +class CardTou { + private val locale: Locale = ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0) + + fun getUrl(card: Card): Uri? { + val issuerName = card.cardData?.issuerName ?: return null + if (issuerName.toLowerCase() != "start2coin") return null + + val baseUrl = "https://app.tangem.com/tou/" + val regionCode = regionCode(card.cardId) ?: "fr" + val filename = filename(locale.language, regionCode) + return Uri.parse(baseUrl + filename) + } + + private fun filename(languageCode: String, regionCode: String): String { + return when { + languageCode == "fr" && regionCode == "ch" -> "Start2Coin-fr-ch-tangem.pdf" + languageCode == "de" && regionCode == "ch" -> "Start2Coin-de-ch-tangem.pdf" + languageCode == "en" && regionCode == "ch" -> "Start2Coin-en-ch-tangem.pdf" + languageCode == "it" && regionCode == "ch" -> "Start2Coin-it-ch-tangem.pdf" + languageCode == "fr" && regionCode == "fr" -> "Start2Coin-fr-fr-atangem.pdf" + languageCode == "de" && regionCode == "at" -> "Start2Coin-de-at-tangem.pdf" + 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 2ec73c0f00..65a2c7dff6 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -6,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 wallet: Wallet?, 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 805d31f8c9..ebb0485c03 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -73,7 +73,8 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta action.fiatCurrencyName ), securityScreenState = SecurityScreenState(currentOption = securityOption), - createTwinWalletState = twinsState + 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 4730507ebc..7995acdcdb 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.details.redux +import android.net.Uri import com.tangem.blockchain.common.Wallet import com.tangem.commands.common.card.Card import com.tangem.tap.common.entities.Button @@ -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/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 262482b360..8578a60304 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -1,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 @@ -80,6 +81,13 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber 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/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index ca47b5613e..8829e7289a 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/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 20d3988515..44a139c4d1 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 @@ -184,22 +184,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/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 749486e3fe..c3589d9a2b 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 @@ -20,6 +20,7 @@ import com.tangem.tangem_sdk_new.extensions.dpToPx import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction +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.* @@ -316,6 +317,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber + + + app:layout_constraintTop_toBottomOf="@id/tv_card_tou" /> The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning. Tap the card #%s You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet. + Card terms of use The twin address was successfully created