Updated on 2026-08-14
This commit is contained in:
commit
a42729b839
24 changed files with 336 additions and 105 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -6,6 +6,9 @@
|
|||
# Local configuration file (sdk path, etc)
|
||||
local.properties
|
||||
|
||||
# Google services
|
||||
|
||||
|
||||
# Gradle generated files
|
||||
.gradle
|
||||
|
||||
|
|
|
|||
|
|
@ -75,13 +75,9 @@ dependencies {
|
|||
implementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
// TODO: change to prod dependencies when they are available
|
||||
// implementation 'com.tangem:blockchain:1.164.0'
|
||||
// implementation 'com.tangem:core:1.109.0'
|
||||
// implementation 'com.tangem:sdk:1.109.0'
|
||||
implementation 'blockchain-sdk-kotlin:blockchain:tangem-20210416.151414-8'
|
||||
implementation 'com.tangem:tangem-core:tangem-20210414.185224-2'
|
||||
implementation 'com.tangem:tangem-sdk:tangem-20210414.185231-2'
|
||||
implementation 'com.tangem:blockchain:develop-5'
|
||||
implementation 'com.tangem:core:develop-10'
|
||||
implementation 'com.tangem:sdk:develop-10'
|
||||
|
||||
// WebView
|
||||
implementation "androidx.browser:browser:1.3.0"
|
||||
|
|
|
|||
|
|
@ -629,6 +629,13 @@
|
|||
"name" : "QASH",
|
||||
"contractAddress" : "0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6"
|
||||
},
|
||||
{
|
||||
"decimalCount" : 8,
|
||||
"symbol" : "QCX",
|
||||
"name" : "QuickX Protocol",
|
||||
"contractAddress" : "0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee",
|
||||
"customIcon": "qcx"
|
||||
},
|
||||
{
|
||||
"decimalCount" : 8,
|
||||
"symbol" : "QRL",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.squareup.picasso.Callback
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.IconsUtil
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.wallet.R
|
||||
|
||||
fun Picasso.loadCurrenciesIcon(
|
||||
imageView: ImageView,
|
||||
textView: TextView,
|
||||
token: Token? = null,
|
||||
blockchain: Blockchain?,
|
||||
) {
|
||||
val blockchain = blockchain ?: Blockchain.Ethereum
|
||||
|
||||
val url = if (token != null) {
|
||||
IconsUtil.getTokenIconUri(blockchain, token)
|
||||
} else {
|
||||
IconsUtil.getBlockchainIconUri(blockchain)
|
||||
}
|
||||
|
||||
imageView.setImageDrawable(null)
|
||||
imageView.colorFilter = null
|
||||
textView.text = null
|
||||
|
||||
when {
|
||||
url != null -> {
|
||||
this.load(url)
|
||||
.placeholder(R.drawable.shape_circle)
|
||||
?.into(imageView,
|
||||
object : Callback {
|
||||
override fun onError(e: Exception?) {
|
||||
setOfflineCurrencyImage(imageView, textView, token, blockchain)
|
||||
}
|
||||
|
||||
override fun onSuccess() {
|
||||
}
|
||||
})
|
||||
}
|
||||
token?.symbol == QCX -> {
|
||||
this.load(R.drawable.ic_qcx)?.into(imageView)
|
||||
}
|
||||
else -> {
|
||||
setOfflineCurrencyImage(imageView, textView, token, blockchain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val QCX = "QCX"
|
||||
|
||||
private fun setOfflineCurrencyImage(
|
||||
imageView: ImageView,
|
||||
textView: TextView,
|
||||
token: Token?,
|
||||
blockchain: Blockchain,
|
||||
) {
|
||||
if (token != null) {
|
||||
setTokenImage(imageView, textView, token)
|
||||
} else {
|
||||
setBlockchainImage(imageView, textView, blockchain)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setBlockchainImage(
|
||||
imageView: ImageView,
|
||||
textView: TextView,
|
||||
blockchain: Blockchain
|
||||
) {
|
||||
imageView.setImageResource(blockchain.getIconRes())
|
||||
imageView.colorFilter = null
|
||||
textView.text = null
|
||||
}
|
||||
|
||||
private fun setTokenImage(
|
||||
imageView: ImageView,
|
||||
textView: TextView,
|
||||
token: Token
|
||||
) {
|
||||
imageView.setImageResource(R.drawable.shape_circle)
|
||||
imageView.setColorFilter(token.getColor())
|
||||
textView.text = token.symbol.take(1)
|
||||
}
|
||||
|
|
@ -199,13 +199,12 @@ class TapWalletManager {
|
|||
store.dispatch(WalletAction.MultiWallet.FindBlockchainsInUse(card, walletManagerFactory))
|
||||
store.dispatch(WalletAction.MultiWallet.FindTokensInUse)
|
||||
} else {
|
||||
val blockchains = listOfNotNull(primaryBlockchain) + savedCurrencies.blockchains
|
||||
val blockchains = savedCurrencies.blockchains
|
||||
val walletManagers = walletManagerFactory.makeWalletManagersForApp(card, blockchains)
|
||||
val tokens = presetTokens + savedCurrencies.tokens
|
||||
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers))
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchains(blockchains))
|
||||
store.dispatch(WalletAction.MultiWallet.AddTokens(tokens))
|
||||
store.dispatch(WalletAction.MultiWallet.AddTokens(savedCurrencies.tokens))
|
||||
store.dispatch(WalletAction.MultiWallet.FindTokensInUse)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.tap.domain.configurable.warningMessage
|
||||
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import com.squareup.moshi.Json
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
|
||||
|
|
@ -7,15 +9,16 @@ import com.tangem.blockchain.common.Blockchain
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class WarningMessage(
|
||||
val title: String,
|
||||
val message: String,
|
||||
val type: Type,
|
||||
val priority: Priority,
|
||||
val location: List<Location>,
|
||||
private val blockchains: List<String>?,
|
||||
val titleResId: Int? = null,
|
||||
val messageResId: Int? = null,
|
||||
val origin: Origin = Origin.Remote,
|
||||
val title: String,
|
||||
val message: String,
|
||||
val type: Type,
|
||||
val priority: Priority,
|
||||
val location: List<Location>,
|
||||
private val blockchains: List<String>?,
|
||||
@StringRes val titleResId: Int? = null,
|
||||
@StringRes val messageResId: Int? = null,
|
||||
val origin: Origin = Origin.Remote,
|
||||
@StringRes val buttonTextId: Int? = null,
|
||||
) {
|
||||
val blockchainList: List<Blockchain>? by lazy {
|
||||
blockchains?.map { Blockchain.fromId(it.toUpperCase()) }
|
||||
|
|
@ -43,6 +46,7 @@ data class WarningMessage(
|
|||
Temporary, // можно скрыть (кнопка ОК)
|
||||
|
||||
AppRating
|
||||
|
||||
}
|
||||
|
||||
enum class Location {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.tangem.tap.domain.configurable.warningMessage
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tangem_sdk_new.ui.animation.VoidCallback
|
||||
import com.tangem.tap.common.extensions.containsAny
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
|
|
@ -98,6 +100,19 @@ class WarningMessagesManager(
|
|||
WarningMessage.Origin.Local
|
||||
)
|
||||
|
||||
fun signedHashesMultiWalletWarning(): WarningMessage = WarningMessage(
|
||||
title = "",
|
||||
message = "",
|
||||
type = WarningMessage.Type.Temporary,
|
||||
priority = WarningMessage.Priority.Info,
|
||||
location = listOf(WarningMessage.Location.MainScreen),
|
||||
blockchains = null,
|
||||
titleResId = R.string.warning_important_security_info,
|
||||
messageResId = R.string.warning_signed_tx_previously,
|
||||
origin = WarningMessage.Origin.Local,
|
||||
buttonTextId = R.string.warning_button_learn_more,
|
||||
)
|
||||
|
||||
fun appRatingWarning(): WarningMessage = WarningMessage(
|
||||
"",
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsS
|
|||
return when (action) {
|
||||
DetailsAction.EraseWallet.Check -> {
|
||||
val notAllowedByCard = state.card?.settingsMask?.contains(Settings.ProhibitPurgeWallet) == true
|
||||
|| state.card?.settingsMask?.contains(Settings.IsReusable) == false
|
||||
val notEmpty = state.wallets.any {
|
||||
!it.recentTransactions.isNullOrEmpty() || it.amounts.toSendableAmounts().isNotEmpty()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import androidx.annotation.StringRes
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.squareup.picasso.Callback
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.IconsUtil
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.global.CryptoCurrencyName
|
||||
|
|
@ -16,6 +19,7 @@ import com.tangem.tap.store
|
|||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.item_currency_subtitle.view.*
|
||||
import kotlinx.android.synthetic.main.item_popular_token.view.*
|
||||
import java.lang.Exception
|
||||
import java.util.*
|
||||
|
||||
class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>(DiffUtilCallback) {
|
||||
|
|
@ -104,9 +108,11 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
view.btn_add_token.show(!isAdded)
|
||||
view.btn_token_added.show(isAdded)
|
||||
|
||||
view.iv_currency.setImageResource(currency.blockchain.getIconRes())
|
||||
view.iv_currency.colorFilter = null
|
||||
view.tv_token_letter.text = null
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = view.iv_currency,
|
||||
textView = view.tv_token_letter,
|
||||
blockchain = blockchain, token = null
|
||||
)
|
||||
|
||||
view.btn_add_token.setOnClickListener {
|
||||
store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchain))
|
||||
|
|
@ -123,10 +129,11 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
|
|||
view.btn_add_token.show(!isAdded)
|
||||
view.btn_token_added.show(isAdded)
|
||||
|
||||
view.iv_currency.setImageResource(R.drawable.shape_circle)
|
||||
view.iv_currency.setColorFilter(token.getColor())
|
||||
view.tv_token_letter.text = token.symbol.take(1)
|
||||
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = view.iv_currency,
|
||||
textView = view.tv_token_letter,
|
||||
token = token, blockchain = Blockchain.Ethereum
|
||||
)
|
||||
view.btn_add_token.setOnClickListener {
|
||||
store.dispatch(WalletAction.MultiWallet.AddToken(token))
|
||||
view.btn_add_token.hide()
|
||||
|
|
@ -152,7 +159,10 @@ sealed class CurrencyListItem {
|
|||
data class TitleListItem(@StringRes val titleResId: Int) : CurrencyListItem()
|
||||
|
||||
companion object {
|
||||
fun createListOfCurrencies(blockchains: List<Blockchain>, tokens: List<Token>): List<CurrencyListItem> {
|
||||
fun createListOfCurrencies(
|
||||
blockchains: List<Blockchain>,
|
||||
tokens: List<Token>
|
||||
): List<CurrencyListItem> {
|
||||
val blockchainsTitle = R.string.add_tokens_subtitle_blockchains
|
||||
val tokensTitle = R.string.add_tokens_subtitle_tokens
|
||||
return listOf(TitleListItem(blockchainsTitle)) +
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ sealed class WalletAction : Action {
|
|||
object ShowDialog : WalletAction() {
|
||||
object QrCode : WalletAction()
|
||||
object ScanFails : WalletAction()
|
||||
object SignedHashesMultiWalletDialog : WalletAction()
|
||||
}
|
||||
|
||||
object HideDialog : WalletAction()
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ data class WalletState(
|
|||
&& (walletData.token == null || walletData.token != store.state.walletState.primaryToken)) {
|
||||
val walletManager = getWalletManager(walletData.currencyData.currencySymbol)
|
||||
?: return true
|
||||
|
||||
if (walletData.token == null && walletManager.presetTokens.isNotEmpty()) return false
|
||||
|
||||
val wallet = walletManager.wallet
|
||||
if (walletData.blockchain != null) {
|
||||
return wallet.recentTransactions.toPendingTransactions(wallet.address).isEmpty() &&
|
||||
|
|
@ -112,7 +115,8 @@ sealed class WalletDialog: StateDialog {
|
|||
) : WalletDialog()
|
||||
|
||||
data class SelectAmountToSendDialog(val amounts: List<Amount>?) : WalletDialog()
|
||||
object ScanFailsDialog: WalletDialog()
|
||||
object ScanFailsDialog : WalletDialog()
|
||||
object SignedHashesMultiWalletDialog : WalletDialog()
|
||||
}
|
||||
|
||||
enum class ProgressState { Loading, Done, Error }
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ class MultiWalletMiddleware {
|
|||
globalState?.scanNoteResponse?.card?.cardId?.let {
|
||||
currenciesRepository.saveAddedToken(it, action.token)
|
||||
}
|
||||
addToken(action.token, walletState)
|
||||
addToken(action.token, walletState, globalState)
|
||||
}
|
||||
is WalletAction.MultiWallet.AddTokens -> {
|
||||
action.tokens.map { addToken(it, walletState) }
|
||||
action.tokens.map { addToken(it, walletState, globalState) }
|
||||
}
|
||||
is WalletAction.MultiWallet.AddBlockchain -> {
|
||||
globalState?.scanNoteResponse?.card?.let { card ->
|
||||
|
|
@ -62,6 +62,8 @@ class MultiWalletMiddleware {
|
|||
is WalletAction.MultiWallet.RemoveWallet -> {
|
||||
val cardId = globalState?.scanNoteResponse?.card?.cardId
|
||||
if (action.walletData.token != null) {
|
||||
walletState?.getWalletManagerForToken(action.walletData.token.symbol)
|
||||
?.removeToken(action.walletData.token)
|
||||
cardId?.let { currenciesRepository.removeToken(it, action.walletData.token) }
|
||||
} else if (action.walletData.blockchain != null) {
|
||||
cardId?.let { currenciesRepository.removeBlockchain(it, action.walletData.blockchain) }
|
||||
|
|
@ -120,8 +122,15 @@ class MultiWalletMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun addToken(token: Token, walletState: WalletState?) {
|
||||
val walletManager = walletState?.getWalletManager(token.symbol)
|
||||
private fun addToken(token: Token, walletState: WalletState?, globalState: GlobalState?) {
|
||||
val card = globalState?.scanNoteResponse?.card ?: return
|
||||
val walletManager = walletState?.getWalletManager(token.symbol) ?:
|
||||
globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp(
|
||||
card = card,
|
||||
blockchain = Blockchain.Ethereum
|
||||
)?.also { walletManager ->
|
||||
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager))
|
||||
}
|
||||
scope.launch {
|
||||
val result = walletManager?.addToken(token)
|
||||
withContext(Dispatchers.Main) {
|
||||
|
|
|
|||
|
|
@ -87,9 +87,18 @@ class WarningsMiddleware {
|
|||
): WarningMessage? {
|
||||
if (card.isTwinCard()) return null
|
||||
|
||||
if (card.isMultiwalletAllowed) {
|
||||
return if (card.hasSignedHashes()) {
|
||||
WarningMessagesManager.signedHashesMultiWalletWarning()
|
||||
} else {
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val validator = store.state.walletState.walletManagers.firstOrNull()
|
||||
as? SignatureCountValidator
|
||||
return if (validator == null || card.isMultiwalletAllowed) {
|
||||
return if (validator == null) {
|
||||
if (card.hasSignedHashes()) {
|
||||
WarningMessagesManager.alreadySignedHashesWarning()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -225,6 +225,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
is WalletAction.ShowDialog.ScanFails -> {
|
||||
newState = newState.copy(walletDialog = WalletDialog.ScanFailsDialog)
|
||||
}
|
||||
is WalletAction.ShowDialog.SignedHashesMultiWalletDialog -> {
|
||||
newState = newState.copy(walletDialog = WalletDialog.SignedHashesMultiWalletDialog)
|
||||
}
|
||||
is WalletAction.HideDialog -> {
|
||||
newState = newState.copy(walletDialog = null)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@ import android.view.ViewGroup
|
|||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.squareup.picasso.Callback
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.IconsUtil
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.common.extensions.getIconRes
|
||||
import com.tangem.tap.common.extensions.loadCurrenciesIcon
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
|
|
@ -17,6 +21,11 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus
|
|||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.item_currency_wallet.view.*
|
||||
import kotlinx.android.synthetic.main.item_currency_wallet.view.iv_currency
|
||||
import kotlinx.android.synthetic.main.item_currency_wallet.view.tv_currency_symbol
|
||||
import kotlinx.android.synthetic.main.item_currency_wallet.view.tv_token_letter
|
||||
import kotlinx.android.synthetic.main.item_popular_token.view.*
|
||||
import java.lang.Exception
|
||||
import java.math.BigDecimal
|
||||
|
||||
class WalletAdapter
|
||||
|
|
@ -89,16 +98,15 @@ class WalletAdapter
|
|||
view.card_wallet.setOnClickListener {
|
||||
store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet))
|
||||
}
|
||||
val blockchain = wallet.currencyData.currencySymbol?.let { Blockchain.fromCurrency(it) }
|
||||
if (blockchain != null && blockchain != Blockchain.Unknown) {
|
||||
view.tv_token_letter.text = null
|
||||
view.iv_currency.colorFilter = null
|
||||
view.iv_currency.setImageResource(blockchain.getIconRes())
|
||||
} else {
|
||||
view.tv_token_letter.text = wallet.currencyData.currencySymbol?.take(1)
|
||||
wallet.token?.getColor()?.let { view.iv_currency.setColorFilter(it) }
|
||||
view.iv_currency.setImageResource(R.drawable.shape_circle)
|
||||
}
|
||||
val blockchain = wallet.blockchain
|
||||
val token = wallet.token
|
||||
|
||||
Picasso.get().loadCurrenciesIcon(
|
||||
imageView = view.iv_currency,
|
||||
textView = view.tv_token_letter,
|
||||
token = token, blockchain = blockchain
|
||||
)
|
||||
|
||||
when (wallet.currencyData.status) {
|
||||
BalanceStatus.VerifiedOnline, BalanceStatus.SameCurrencyTransactionInProgress -> hideWarning()
|
||||
BalanceStatus.Loading -> {
|
||||
|
|
|
|||
|
|
@ -37,9 +37,11 @@ class WarningMessagesAdapter : ListAdapter<WarningMessage, WarningMessageVH>(Dif
|
|||
}
|
||||
|
||||
object DiffUtilCallback : DiffUtil.ItemCallback<WarningMessage>() {
|
||||
override fun areContentsTheSame(oldItem: WarningMessage, newItem: WarningMessage) = oldItem == newItem
|
||||
override fun areContentsTheSame(oldItem: WarningMessage, newItem: WarningMessage) =
|
||||
oldItem == newItem
|
||||
|
||||
override fun areItemsTheSame(oldItem: WarningMessage, newItem: WarningMessage) = oldItem == newItem
|
||||
override fun areItemsTheSame(oldItem: WarningMessage, newItem: WarningMessage) =
|
||||
oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +54,8 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
|
|||
}
|
||||
|
||||
private fun setText(warning: WarningMessage) {
|
||||
fun getString(resId: Int?, default: String) = if (resId == null) default else view.getString(resId)
|
||||
fun getString(resId: Int?, default: String) =
|
||||
if (resId == null) default else view.getString(resId)
|
||||
|
||||
view.tv_title.text = getString(warning.titleResId, warning.title)
|
||||
view.tv_message.text = getString(warning.messageResId, warning.message)
|
||||
|
|
@ -67,63 +70,83 @@ class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
|
|||
view.card_view.setCardBackgroundColor(view.context.resources.getColor(color))
|
||||
}
|
||||
|
||||
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 {
|
||||
val activity = view.context.getActivity() ?: return@setOnClickListener
|
||||
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE)
|
||||
store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
|
||||
val reviewManager = ReviewManagerFactory.create(activity)
|
||||
val task = reviewManager.requestReviewFlow()
|
||||
task.addOnCompleteListener {
|
||||
if (it.isSuccessful) {
|
||||
val reviewFlow = reviewManager.launchReviewFlow(activity, it.result)
|
||||
reviewFlow.addOnCompleteListener {
|
||||
if (it.isSuccessful) {
|
||||
// send review was succeed
|
||||
} else {
|
||||
// send fails
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.e(task.exception)
|
||||
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()
|
||||
val buttonAction =
|
||||
when (warning.titleResId) {
|
||||
R.string.warning_important_security_info -> {
|
||||
View.OnClickListener {
|
||||
store.dispatch(WalletAction.ShowDialog.SignedHashesMultiWalletDialog)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
View.OnClickListener {
|
||||
store.dispatch(GlobalAction.HideWarningMessage(warning))
|
||||
}
|
||||
}.addOnFailureListener {
|
||||
Timber.e(it)
|
||||
}
|
||||
store.dispatch(GlobalAction.HideWarningMessage(warning))
|
||||
}
|
||||
val buttonTitle = view.getString(
|
||||
warning.buttonTextId ?: R.string.how_to_got_it_button
|
||||
)
|
||||
view.btn_got_it.setOnClickListener (buttonAction)
|
||||
view.btn_got_it.text = buttonTitle
|
||||
}
|
||||
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 {
|
||||
val activity = view.context.getActivity() ?: return@setOnClickListener
|
||||
|
||||
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE)
|
||||
store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
|
||||
val reviewManager = ReviewManagerFactory.create(activity)
|
||||
val task = reviewManager.requestReviewFlow()
|
||||
task.addOnCompleteListener {
|
||||
if (it.isSuccessful) {
|
||||
val reviewFlow = reviewManager.launchReviewFlow(activity, it.result)
|
||||
reviewFlow.addOnCompleteListener {
|
||||
if (it.isSuccessful) {
|
||||
// send review was succeed
|
||||
} else {
|
||||
// send fails
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Timber.e(task.exception)
|
||||
}
|
||||
}.addOnFailureListener {
|
||||
Timber.e(it)
|
||||
}
|
||||
store.dispatch(GlobalAction.HideWarningMessage(warning))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SpacesItemDecoration(private val spacePx: Int) : ItemDecoration() {
|
||||
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
outRect.left = spacePx
|
||||
outRect.right = spacePx
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
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.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
class SignedHashesWarningDialog {
|
||||
companion object {
|
||||
fun create(context: Context): AlertDialog {
|
||||
return AlertDialog.Builder(context).apply {
|
||||
setTitle(context.getString(R.string.warning_important_security_info))
|
||||
setMessage(R.string.alert_signed_hashes_message)
|
||||
setPositiveButton(R.string.alert_button_i_understand) { _, _ ->
|
||||
store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
|
||||
store.dispatch(
|
||||
GlobalAction.HideWarningMessage(
|
||||
WarningMessagesManager.signedHashesMultiWalletWarning()
|
||||
)
|
||||
)
|
||||
}
|
||||
setNegativeButton(R.string.common_cancel) { _, _ -> }
|
||||
setOnDismissListener {
|
||||
store.dispatch(WalletAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus
|
|||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.card_balance.*
|
||||
|
|
@ -94,24 +95,24 @@ class MultiWalletView : WalletView {
|
|||
when (state.primaryWallet?.currencyData?.status) {
|
||||
BalanceStatus.EmptyCard -> {
|
||||
showErrorState(
|
||||
fragment,
|
||||
fragment.getText(R.string.wallet_error_empty_card),
|
||||
fragment.getString(R.string.wallet_error_empty_card_subtitle)
|
||||
fragment,
|
||||
fragment.getText(R.string.wallet_error_empty_card),
|
||||
fragment.getString(R.string.wallet_error_empty_card_subtitle)
|
||||
)
|
||||
configureButtonsForEmptyWalletState(fragment)
|
||||
}
|
||||
BalanceStatus.UnknownBlockchain -> {
|
||||
showErrorState(
|
||||
fragment,
|
||||
fragment.getText(R.string.wallet_error_unsupported_blockchain),
|
||||
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle)
|
||||
fragment,
|
||||
fragment.getText(R.string.wallet_error_unsupported_blockchain),
|
||||
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showErrorState(
|
||||
fragment: WalletFragment, errorTitle: CharSequence, errorDescription: CharSequence,
|
||||
fragment: WalletFragment, errorTitle: CharSequence, errorDescription: CharSequence,
|
||||
) = with(fragment) {
|
||||
l_card_balance.show()
|
||||
l_balance.hide()
|
||||
|
|
@ -137,6 +138,11 @@ class MultiWalletView : WalletView {
|
|||
is WalletDialog.ScanFailsDialog -> {
|
||||
if (dialog == null) dialog = ScanFailsDialog.create(context).apply { show() }
|
||||
}
|
||||
is WalletDialog.SignedHashesMultiWalletDialog -> {
|
||||
if (dialog == null) {
|
||||
dialog = SignedHashesWarningDialog.create(context).apply { show() }
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
dialog?.dismiss()
|
||||
dialog = null
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
|
|||
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.QrDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
|
||||
import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.card_balance.*
|
||||
|
|
@ -238,6 +239,11 @@ class SingleWalletView : WalletView {
|
|||
is WalletDialog.ScanFailsDialog -> {
|
||||
if (dialog == null) dialog = ScanFailsDialog.create(context).apply { show() }
|
||||
}
|
||||
is WalletDialog.SignedHashesMultiWalletDialog -> {
|
||||
if (dialog == null) {
|
||||
dialog = SignedHashesWarningDialog.create(context).apply { show() }
|
||||
}
|
||||
}
|
||||
null -> {
|
||||
dialog?.dismiss()
|
||||
dialog = null
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ class NetworkConnectivity(
|
|||
(capabilities != null) &&
|
||||
(capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN) ||
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET))
|
||||
|
||||
} else {
|
||||
|
|
|
|||
BIN
app/src/main/res/drawable-ldpi/ic_qcx.png
Normal file
BIN
app/src/main/res/drawable-ldpi/ic_qcx.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
|
|
@ -103,5 +103,12 @@ this wallet.
|
|||
<string name="warning_failed_to_verify_card_message" translatable="false">This card might be a production sample or counterfeit</string>
|
||||
|
||||
|
||||
|
||||
<string name="warning_important_security_info" translatable="false">Important security information \u26A0</string>
|
||||
<string name="warning_signed_tx_previously" translatable="false">This card has signed transactions in the past</string>
|
||||
<string name="warning_button_learn_more" translatable="false">Learn more</string>
|
||||
<string name="alert_signed_hashes_message" translatable="false">This card is not a bearer note. We can’t currently match the signature count on the card with the information on the blockchain. This is normal but in rare cases can mean a previous holder is holding back an offline signature, which is a security concern.
|
||||
\n\nDo not accept this card as physical payment from someone you don’t trust.
|
||||
\n\nIt’s perfectly safe in all other respects.
|
||||
\n\nTangem is the only hardware wallet to offer signature count protection.</string>
|
||||
<string name="alert_button_i_understand" translatable="false">I understand</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
ext.versions = [
|
||||
kotlin : '1.4.31',
|
||||
build_gradle: '4.1.3',
|
||||
kotlin : '1.5.0',
|
||||
build_gradle: '4.2.0',
|
||||
]
|
||||
|
|
|
|||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue