Updated on 2026-08-14

This commit is contained in:
Tangem 2021-02-26 10:55:13 +03:00
commit 59f8413da2
12 changed files with 117 additions and 39 deletions

View file

@ -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
}
}

View file

@ -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<FiatCurrencyName>? = null,
) : DetailsAction()

View file

@ -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)
)
}

View file

@ -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<SecurityOption> = EnumSet.allOf(SecurityOption::class.java),
val buttonProceed: Button = Button(true)
val buttonProceed: Button = Button(true),
)
enum class SecurityOption { LongTap, PassCode, AccessCode }

View file

@ -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<Det
tv_disclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
tv_card_tou.show(state.cardTermsOfUseUrl != null)
tv_card_tou.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = state.cardTermsOfUseUrl
startActivity(intent)
}
if (state.createTwinWalletState != null) {
if (state.createTwinWalletState.allowRecreatingWallet == true) {
tv_erase_wallet.show()

View file

@ -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? {

View file

@ -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<String, String> {
return this.split(firstDelimiter)
.map { it.split(secondDelimiter) }
.map { it.first() to it.last().toString() }
.toMap()
}

View file

@ -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() {

View file

@ -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)

View file

@ -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<Walle
scanNoteResponse.card, scanNoteResponse,
store.state.walletState.wallet,
store.state.globalState.configManager?.config?.isCreatingTwinCardsAllowed,
CardTou(),
store.state.globalState.appCurrency
))
store.dispatch(NavigationAction.NavigateTo(AppScreen.Details))