From 48cb0332890b12682b79108b2478742176e5a55e Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 4 Apr 2021 23:38:21 +0300 Subject: [PATCH 01/21] Updated on 2026-08-14 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 4dc0d14356..3b07b1a1ac 100644 --- a/build.gradle +++ b/build.gradle @@ -31,14 +31,14 @@ allprojects { username = nexusUser password = nexusPass } - url "https://nexus.tangem.com/repository/maven-releases/" + url "https://nexus.tangem-tech.com/repository/maven-releases/" } maven { credentials { username = nexusUser password = nexusPass } - url "https://nexus.tangem.com/repository/maven-snapshots/" + url "https://nexus.tangem-tech.com/repository/maven-snapshots/" } maven { url 'https://jitpack.io' } } From c63fe7289bfffbf867d51abfea80a0f46ee4396c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Apr 2021 15:15:38 +0300 Subject: [PATCH 02/21] Updated on 2026-08-14 --- .../send/redux/middlewares/AmountMiddleware.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index 08dc775fba..60851b1b83 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -78,11 +78,17 @@ class AmountMiddleware { } private fun setMaxAmount(appState: AppState?, dispatch: (Action) -> Unit) { - val amountState = appState?.sendState?.amountState ?: return + val sendState = appState?.sendState ?: return + + dispatch(AmountAction.SetAmount(sendState.amountState.balanceCrypto, false)) + + if (sendState.addressPayIdState.destinationWalletAddress == null || sendState.addressPayIdState.error != null) { + // if the destination address is empty or it has an error - prevent requesting the fee + return + } - dispatch(AmountAction.SetAmount(amountState.balanceCrypto, false)) dispatch(FeeAction.RequestFee) - if (!amountState.isCoinAmount()) return + if (!sendState.amountState.isCoinAmount()) return dispatch(FeeAction.ChangeLayoutVisibility(main = true, controls = true, chipGroup = true)) dispatch(FeeActionUi.ChangeIncludeFee(true)) From 7919fbdd5e7742a05e74c4e6a0e676da6804e66b Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Apr 2021 19:34:45 +0300 Subject: [PATCH 03/21] Updated on 2026-08-14 --- .../middlewares/AddressPayIdMiddleware.kt | 2 ++ .../redux/middlewares/AmountMiddleware.kt | 21 +++++++++---------- .../redux/middlewares/RequestFeeMiddleware.kt | 3 ++- .../send/redux/reducers/ReceiptReducer.kt | 2 +- .../features/send/redux/states/SendState.kt | 18 +++++++++------- .../tap/features/send/ui/SendFragment.kt | 11 +--------- 6 files changed, 27 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index a713ed3163..3da9747dd3 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 @@ -135,9 +135,11 @@ internal class AddressPayIdMiddleware { if (failReason == null) { noSchemeAddress.getQueryParameter("amount")?.toBigDecimalOrNull()?.let { dispatch(AmountAction.SetAmount(it, false)) + dispatch(AmountActionUi.CheckAmountToSend) } dispatch(SetWalletAddress(supposedAddress, isUserInput)) dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, null)) + dispatch(FeeAction.RequestFee) } else { dispatch(SetAddressError(failReason)) dispatch(TransactionExtrasAction.Release) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index 60851b1b83..19bb270b16 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -6,6 +6,7 @@ import com.tangem.common.extensions.isZero import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.states.MainCurrencyType +import com.tangem.tap.features.send.redux.states.SendState import org.rekotlin.Action import java.math.BigDecimal @@ -43,6 +44,7 @@ class AmountMiddleware { } dispatch(AmountAction.SetAmount(inputValueCrypto, true)) + dispatch(AmountActionUi.CheckAmountToSend) dispatch(FeeAction.RequestFee) } @@ -81,18 +83,15 @@ class AmountMiddleware { val sendState = appState?.sendState ?: return dispatch(AmountAction.SetAmount(sendState.amountState.balanceCrypto, false)) - - if (sendState.addressPayIdState.destinationWalletAddress == null || sendState.addressPayIdState.error != null) { - // if the destination address is empty or it has an error - prevent requesting the fee - return - } - - dispatch(FeeAction.RequestFee) - if (!sendState.amountState.isCoinAmount()) return - - dispatch(FeeAction.ChangeLayoutVisibility(main = true, controls = true, chipGroup = true)) - dispatch(FeeActionUi.ChangeIncludeFee(true)) dispatch(AmountActionUi.CheckAmountToSend) + + if (SendState.isReadyToRequestFee()) { + dispatch(FeeAction.RequestFee) + if (!sendState.amountState.isCoinAmount()) return + + dispatch(FeeAction.ChangeLayoutVisibility(main = true, controls = true, chipGroup = true)) + dispatch(FeeActionUi.ChangeIncludeFee(true)) + } } private fun toggleMainCurrency(appState: AppState?, dispatch: (Action) -> Unit) { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt index 49b02cfea8..b2523e9e7a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt @@ -10,6 +10,7 @@ import com.tangem.tap.features.send.redux.AmountActionUi import com.tangem.tap.features.send.redux.FeeAction import com.tangem.tap.features.send.redux.ReceiptAction import com.tangem.tap.features.send.redux.SendAction +import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.scope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -26,7 +27,7 @@ class RequestFeeMiddleware { val sendState = appState?.sendState ?: return val walletManager = sendState.walletManager ?: return - if (!sendState.addressPayIdIsReady()) { + if (!SendState.isReadyToRequestFee()) { dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.ADDRESS_OR_AMOUNT_IS_EMPTY)) dispatch(FeeAction.ChangeLayoutVisibility(main = false, chipGroup = true)) dispatch(ReceiptAction.RefreshReceipt) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt index 1ecf9ea6bb..0a4a4df672 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt @@ -41,7 +41,7 @@ class ReceiptReducer : SendInternalReducer { val layoutType = determineLayoutType(amountState.mainCurrency.type, amountState.typeOfAmount) val symbols = determineSymbols(wallet) - val showBlank = !sendState.isReadyToSend() + val showBlank = !SendState.isReadyToSend() val result = state.copy( visibleTypeOfReceipt = layoutType, mainCurrency = amountState.mainCurrency, diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index c4e57d708a..c2ec80e66a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -45,13 +45,6 @@ data class SendState( override val stateId: StateId = StateId.SEND_SCREEN - fun isReadyToSend(): Boolean { - val sendState = store.state.sendState - return addressPayIdIsReady() && sendState.amountState.isReady() && sendState.feeState.isReady() - } - - fun addressPayIdIsReady(): Boolean = store.state.sendState.addressPayIdState.isReady() - fun getDecimals(type: MainCurrencyType): Int = when (type) { MainCurrencyType.FIAT -> 2 MainCurrencyType.CRYPTO -> amountState.amountToExtract?.decimals ?: 0 @@ -110,6 +103,17 @@ data class SendState( AmountType.Reserve -> false } } + + companion object { + fun addressPayIdIsReady(): Boolean = store.state.sendState.addressPayIdState.isReady() + + fun amountIsReady(): Boolean = store.state.sendState.amountState.isReady() + + fun isReadyToRequestFee(): Boolean = addressPayIdIsReady() && amountIsReady() + + fun isReadyToSend(): Boolean = addressPayIdIsReady() && amountIsReady() && + store.state.sendState.feeState.isReady() + } } enum class SendButtonState { 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 6ee78a5baf..0733f10e8a 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 @@ -100,14 +100,12 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { .filter { store.state.sendState.addressPayIdState.viewFieldValue.value != it } .onEach { store.dispatch(AddressPayIdActionUi.HandleUserInput(it)) - store.dispatch(FeeAction.RequestFee) } .launchIn(mainScope) imvPaste.setOnClickListener { store.dispatch(PasteAddressPayId(requireContext().getFromClipboard()?.toString() ?: "")) store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) - store.dispatch(FeeAction.RequestFee) } imvQrCode.setOnClickListener { startActivityForResult( @@ -127,12 +125,6 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { .onEach { store.dispatch(TransactionExtrasAction.XlmMemo.HandleUserInput(it)) } .launchIn(mainScope) -// groupMemo.setOnCheckedChangeListener { group, checkedId -> -// if (checkedId == -1) return@setOnCheckedChangeListener -// -// store.dispatch(TransactionExtrasAction.XlmMemo.ChangeSelectedMemo(MemoUiHelper.toType(checkedId))) -// } - etDestinationTag.inputtedTextAsFlow() .debounce(400) .filter { @@ -155,7 +147,6 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { imvQrCode.postDelayed({ store.dispatch(PasteAddressPayId(scannedCode)) store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) - store.dispatch(FeeAction.RequestFee) }, 200) } @@ -177,7 +168,6 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { etAmountToSend.clearFocus() etAmountToSend.postDelayed(200) { etAmountToSend.hideSoftKeyboard() } store.dispatch(SetMaxAmount) - store.dispatch(CheckAmountToSend) } var snackbarControlledByChangingFocus = false keyboardObserver = KeyboardObserver(requireActivity()) @@ -230,6 +220,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { flExpandCollapse.setOnClickListener { store.dispatch(ToggleControlsVisibility) } + chipGroup.check(FeeUiHelper.toId(FeeType.NORMAL)) chipGroup.setOnCheckedChangeListener { group, checkedId -> if (checkedId == -1) return@setOnCheckedChangeListener From 259e10bc1eb37075bd8cf927cec4e56b5057c2f0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Apr 2021 21:53:36 +0300 Subject: [PATCH 04/21] Updated on 2026-08-14 --- .../IndeterminateProgressButtonWidget.kt | 48 ++++++ .../tap/common/toggleWidget/ToggleWidget.kt | 85 ---------- .../common/toggleWidget/ViewStateWidget.kt | 16 ++ .../common/toggleWidget/progressModifiers.kt | 145 ------------------ .../tap/features/send/ui/SendFragment.kt | 48 +----- .../stateSubscribers/SendStateSubscriber.kt | 6 +- 6 files changed, 72 insertions(+), 276 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/toggleWidget/ToggleWidget.kt create mode 100644 app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/toggleWidget/progressModifiers.kt diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt new file mode 100644 index 0000000000..c6cb5f22bd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt @@ -0,0 +1,48 @@ +package com.tangem.tap.common.toggleWidget + +import android.graphics.drawable.Drawable +import android.view.View +import com.google.android.material.button.MaterialButton +import com.tangem.tap.common.extensions.hide +import com.tangem.tap.common.extensions.show + +/** +[REDACTED_AUTHOR] + */ +open class IndeterminateProgressButtonWidget( + private val button: MaterialButton, + private val progress: View, + initialState: ProgressState = ProgressState.None +) : ViewStateWidget { + + private var initialButtonText: CharSequence = button.text + private var icon: Drawable? = null + + init { + icon = button.icon + changeState(initialState) + } + + override fun changeState(state: WidgetState) { + val progressState = state as? ProgressState ?: return + + when (progressState) { + is ProgressState.None -> switchToNone() + is ProgressState.Progress -> switchToProgress() + } + } + + protected open fun switchToNone() { + button.isClickable = true + button.icon = icon + button.text = initialButtonText + progress.hide() + } + + protected open fun switchToProgress() { + button.isClickable = false + button.icon = null + button.text = "" + progress.show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/ToggleWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/ToggleWidget.kt deleted file mode 100644 index 153a5eddc8..0000000000 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/ToggleWidget.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.tangem.merchant.common.toggleWidget - -import android.view.View -import android.view.ViewGroup - - -/** -[REDACTED_AUTHOR] - */ -interface ToggleState - -interface StateModifier { - fun stateChanged(container: ViewGroup, view: View, state: ToggleState) -} - -interface ToggleView { - val mainViewModifiers: MutableList - val toggleViewModifiers: MutableList - - fun setState(state: ToggleState, andApply: Boolean = true) - fun applyState() - fun getView(): View - fun getMainView(): View - fun getToggleView(): View -} - -class ToggleWidget : ToggleView { - private val container: ViewGroup - private val mainView: View - private val toggleView: View - - private var state: ToggleState - - constructor( - container: ViewGroup, - mainView: View, - toggleView: View, - initialState: ToggleState, - mainViewModifier: List = mutableListOf(), - loadingViewModifier: List = mutableListOf() - ) { - this.container = container - this.mainView = mainView - this.toggleView = toggleView - this.state = initialState - this.mainViewModifiers.addAll(mainViewModifier) - this.toggleViewModifiers.addAll(loadingViewModifier) - } - - constructor( - container: ViewGroup, - mainViewId: Int, - toggleViewId: Int, - initialState: ToggleState, - mainViewModifier: List = mutableListOf(), - loadingViewModifier: List = mutableListOf() - ) { - this.container = container - this.mainView = container.findViewById(mainViewId) - this.toggleView = container.findViewById(toggleViewId) - this.state = initialState - this.mainViewModifiers.addAll(mainViewModifier) - this.toggleViewModifiers.addAll(loadingViewModifier) - } - - override val mainViewModifiers: MutableList = mutableListOf() - - override val toggleViewModifiers: MutableList = mutableListOf() - - override fun setState(state: ToggleState, andApply: Boolean) { - this.state = state - if (andApply) applyState() - } - - override fun applyState() { - mainViewModifiers.forEach { it.stateChanged(container, mainView, state) } - toggleViewModifiers.forEach { it.stateChanged(container, toggleView, state) } - } - - override fun getView(): View = container - - override fun getMainView(): View = mainView - - override fun getToggleView(): View = toggleView -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt new file mode 100644 index 0000000000..246268de7a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.common.toggleWidget + + +/** +[REDACTED_AUTHOR] + */ +interface WidgetState + +interface ViewStateWidget { + fun changeState(state: WidgetState) +} + +sealed class ProgressState : WidgetState { + object None : ProgressState() + data class Progress(val progress: Int = 0) : ProgressState() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/progressModifiers.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/progressModifiers.kt deleted file mode 100644 index bc66fa830b..0000000000 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/progressModifiers.kt +++ /dev/null @@ -1,145 +0,0 @@ -package com.tangem.tap.common.toggleWidget - -import android.graphics.drawable.Drawable -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import android.widget.TextView -import androidx.core.widget.TextViewCompat -import com.google.android.material.button.MaterialButton -import com.tangem.merchant.common.toggleWidget.StateModifier -import com.tangem.merchant.common.toggleWidget.ToggleState -import com.tangem.tap.common.extensions.beginDelayedTransition - -/** -[REDACTED_AUTHOR] - */ - -sealed class ProgressState : ToggleState { - class Progress : ProgressState() - class None : ProgressState() -} - -class ReplaceTextStateModifier( - private val initialText: String, - private val replaceText: String = "" -) : StateModifier { - - override fun stateChanged(container: ViewGroup, view: View, state: ToggleState) { - val tv = view as? TextView ?: return - - when (state) { - is ProgressState.Progress -> { - tv.text = replaceText - } - is ProgressState.None -> { - tv.text = initialText - } - } - } -} - -class TextViewDrawableStateModifier( - private val initialDrawable: Drawable?, - private val replaceDrawable: Drawable?, - private val position: Int -) : StateModifier { - companion object { - val LEFT = 1 - val RIGHT = 2 - } - - override fun stateChanged(container: ViewGroup, view: View, state: ToggleState) { - val drawable = when (state) { - is ProgressState.Progress -> replaceDrawable - is ProgressState.None -> initialDrawable - else -> null - } - val drawableChanger = getChanger(view) - drawableChanger?.change(drawable, position) - } - - private fun getChanger(view: View): DrawableChanger? = when (view) { - is MaterialButton -> MaterialButtonChanger(view) - is Button -> TextViewChanger(view) - is TextView -> TextViewChanger(view) - else -> null - } - - internal interface DrawableChanger { - fun change(drawable: Drawable?, position: Int) - } - - internal class TextViewChanger(private val view: TextView) : DrawableChanger { - override fun change(drawable: Drawable?, position: Int) { - when (position) { - LEFT -> setLeft(drawable) - RIGHT -> setRight(drawable) - } - } - - private fun setLeft(drawable: Drawable?) { - if (drawable == null) { - TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(view, 0, 0, 0, 0) - } else { - TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(view, drawable, null, null, null) - } - } - - private fun setRight(drawable: Drawable?) { - if (drawable == null) { - TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(view, 0, 0, 0, 0) - } else { - TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(view, null, null, drawable, null) - } - } - } - - internal class MaterialButtonChanger(private val view: MaterialButton) : DrawableChanger { - override fun change(drawable: Drawable?, position: Int) { - when (position) { - LEFT -> setLeft(drawable) - RIGHT -> setRight(drawable) - } - } - - private fun setLeft(drawable: Drawable?) { - view.iconGravity = MaterialButton.ICON_GRAVITY_START - view.icon = drawable - } - - private fun setRight(drawable: Drawable?) { - view.iconGravity = MaterialButton.ICON_GRAVITY_END - view.icon = drawable - } - } -} - -class ShowHideStateModifier( - private val isShowOnLoading: Boolean = true, - private val typeOfHiding: Int = View.INVISIBLE -) : StateModifier { - - override fun stateChanged(container: ViewGroup, view: View, state: ToggleState) { - container.beginDelayedTransition() - view.visibility = when (state) { - is ProgressState.Progress -> if (isShowOnLoading) View.VISIBLE else typeOfHiding - is ProgressState.None -> if (isShowOnLoading) typeOfHiding else View.VISIBLE - else -> return - } - } -} - -class ClickableStateModifier( - private val isClickableOnLoading: Boolean = false -) : StateModifier { - - override fun stateChanged(container: ViewGroup, view: View, state: ToggleState) { - view.isClickable = when (state) { - is ProgressState.Progress -> isClickableOnLoading - is ProgressState.None -> !isClickableOnLoading - else -> return - - } - } -} \ 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 6ee78a5baf..55b9c3ec1b 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 @@ -13,12 +13,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.textfield.TextInputEditText import com.tangem.Message -import com.tangem.merchant.common.toggleWidget.ToggleWidget import com.tangem.tangem_sdk_new.extensions.dpToPx import com.tangem.tangem_sdk_new.extensions.hideSoftKeyboard import com.tangem.tap.common.KeyboardObserver import com.tangem.tap.common.entities.TapCurrency -import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity @@ -55,17 +53,11 @@ import java.text.DecimalFormatSymbols */ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { - lateinit var sendBtn: ToggleWidget + lateinit var sendBtn: ViewStateWidget private lateinit var etAmountToSend: TextInputEditText private lateinit var warningsAdapter: WarningMessagesAdapter - private fun initSendButtonStates() { - sendBtn = ToggleWidget(flSendButtonContainer, btnSend, progress, ProgressState.None()) - sendBtn.setupSendButtonStateModifiers(requireContext()) - sendBtn.setState(ProgressState.None()) - } - private val sendSubscriber = SendStateSubscriber(this) private lateinit var keyboardObserver: KeyboardObserver @@ -80,12 +72,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { setupAmountLayout() setupFeeLayout() setupWarningMessages() + } + private fun initSendButtonStates() { btnSend.setOnClickListener { store.dispatch(SendActionUi.SendAmountToRecipient( Message(getString(R.string.initial_message_sign_header)) )) } + sendBtn = IndeterminateProgressButtonWidget(btnSend, progress) } private fun setupAddressOrPayIdLayout() { @@ -309,37 +304,4 @@ class FeeUiHelper { } } } -} - - -//class MemoUiHelper { -// companion object { -// fun toId(memo: MemoType): Int { -// return when (memo) { -// MemoType.TEXT -> R.id.chipMemoText -// MemoType.ID -> R.id.chipMemoId -// } -// } -// -// fun toType(id: Int): MemoType { -// return when (id) { -// R.id.chipMemoText -> MemoType.TEXT -// R.id.chipMemoId -> MemoType.ID -// else -> MemoType.TEXT -// } -// } -// } -//} - -private fun ToggleWidget.setupSendButtonStateModifiers(context: Context) { - mainViewModifiers.clear() - mainViewModifiers.add(ReplaceTextStateModifier(context.getString(R.string.send_title), "")) - mainViewModifiers.add( - TextViewDrawableStateModifier( - context.getDrawableCompat(R.drawable.ic_arrow_right), null, TextViewDrawableStateModifier.RIGHT - )) - mainViewModifiers.add(ClickableStateModifier()) - toggleViewModifiers.clear() - toggleViewModifiers.add(ShowHideStateModifier()) -} - +} \ No newline at end of file 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 973ed08f8b..933b1580d7 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 @@ -119,15 +119,15 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber when (state.sendButtonState) { SendButtonState.ENABLED -> { fg.btnSend.isEnabled = true - sendFragment.sendBtn.setState(ProgressState.None(), true) + sendFragment.sendBtn.changeState(ProgressState.None) } SendButtonState.DISABLED -> { fg.btnSend.isEnabled = false - sendFragment.sendBtn.setState(ProgressState.None(), true) + sendFragment.sendBtn.changeState(ProgressState.None) } SendButtonState.PROGRESS -> { fg.btnSend.isEnabled = true - sendFragment.sendBtn.setState(ProgressState.Progress(), true) + sendFragment.sendBtn.changeState(ProgressState.Progress()) } } From 0ace9e9509f8c84766addc1cc2bcafe4efdcb936 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Apr 2021 21:55:05 +0300 Subject: [PATCH 05/21] Updated on 2026-08-14 --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c65890f80b..75ca19087f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,9 +76,9 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:1.157.0' - implementation 'com.tangem:core:1.108.0' - implementation 'com.tangem:sdk:1.108.0' + implementation 'com.tangem:blockchain:1.164.0' + implementation 'com.tangem:core:1.109.0' + implementation 'com.tangem:sdk:1.109.0' // WebView implementation "androidx.browser:browser:1.3.0" From 1d38e35df7790b6a646caabfd475796a6d5f0f94 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Apr 2021 15:36:41 +0300 Subject: [PATCH 06/21] Updated on 2026-08-14 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 75ca19087f..76faa09bc5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,7 +76,7 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:1.164.0' + implementation 'com.tangem:blockchain:1.166.0' implementation 'com.tangem:core:1.109.0' implementation 'com.tangem:sdk:1.109.0' From 0571d8bb8083169a80916dcb30431d52ed40e85a Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 22 Apr 2021 13:53:48 +0300 Subject: [PATCH 07/21] Updated on 2026-08-14 --- app/build.gradle | 11 +- .../tap/common/redux/global/GlobalReducer.kt | 10 -- .../com/tangem/tap/domain/TangemSdkManager.kt | 14 +- .../com/tangem/tap/domain/TapWalletManager.kt | 133 +++++++++++------- .../com/tangem/tap/domain/TapWorkarounds.kt | 5 +- ...{BlockchainExtensions.kt => Blockchain.kt} | 12 ++ .../com/tangem/tap/domain/extensions/Card.kt | 44 ++++++ .../domain/extensions/TangemSdkConstants.kt | 8 ++ .../domain/extensions/WalletManagerFactory.kt | 33 +++++ .../domain/tasks/CreateWalletAndRescanTask.kt | 37 +++-- .../tap/domain/tasks/CreateWalletsTask.kt | 64 +++++++++ .../tangem/tap/domain/tasks/ScanNoteTask.kt | 108 ++++++++------ .../tap/domain/tokens/CurrenciesRepository.kt | 19 ++- .../domain/twins/CreateFirstTwinWalletTask.kt | 10 +- .../twins/CreateSecondTwinWalletTask.kt | 10 +- .../tap/domain/twins/FinalizeTwinTask.kt | 2 +- .../twins/WriteProtectedIssuerDataTask.kt | 5 +- .../features/details/redux/DetailsReducer.kt | 3 +- .../features/details/redux/DetailsState.kt | 1 - .../redux/twins/CreateTwinWalletMiddleware.kt | 2 +- .../features/details/ui/DetailsFragment.kt | 4 - .../tap/features/feedback/FeedbackManager.kt | 4 +- .../send/redux/middlewares/SendMiddleware.kt | 8 +- .../send/redux/reducers/SendScreenReducer.kt | 4 +- .../features/tokens/redux/TokensMiddleware.kt | 3 +- .../middlewares/MultiWalletMiddleware.kt | 9 +- .../redux/middlewares/WalletMiddleware.kt | 6 +- .../redux/middlewares/WarningsMiddleware.kt | 24 ++-- .../wallet/ui/wallet/MultiWalletView.kt | 60 ++++---- .../wallet/ui/wallet/SingleWalletView.kt | 119 ++++++++-------- app/src/main/res/layout/fragment_details.xml | 24 +--- 31 files changed, 507 insertions(+), 289 deletions(-) rename app/src/main/java/com/tangem/tap/domain/extensions/{BlockchainExtensions.kt => Blockchain.kt} (60%) create mode 100644 app/src/main/java/com/tangem/tap/domain/extensions/Card.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt diff --git a/app/build.gradle b/app/build.gradle index 76faa09bc5..44a13aa801 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,10 +75,13 @@ dependencies { 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.166.0' - implementation 'com.tangem:core:1.109.0' - implementation 'com.tangem:sdk:1.109.0' +// 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' // WebView implementation "androidx.browser:browser:1.3.0" 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 bfc866a06c..7b7e74d399 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 @@ -25,16 +25,6 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.RestoreAppCurrency.Success -> { globalState.copy(appCurrency = action.appCurrency) } - is GlobalAction.UpdateWalletSignedHashes -> { - val card = globalState.scanNoteResponse?.card?.copy( - walletSignedHashes = action.walletSignedHashes - ) - if (card != null) { - globalState.copy(scanNoteResponse = globalState.scanNoteResponse.copy(card = card)) - } else { - globalState - } - } is GlobalAction.SetConfigManager -> { globalState.copy(configManager = action.configManager) } diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 77d26702ab..e6524859c6 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -2,10 +2,16 @@ package com.tangem.tap.domain import androidx.activity.ComponentActivity import com.tangem.* -import com.tangem.commands.* +import com.tangem.commands.CommandResponse +import com.tangem.commands.PinType +import com.tangem.commands.SetPinCommand +import com.tangem.commands.SetPinResponse import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.CardType +import com.tangem.commands.wallet.PurgeWalletCommand +import com.tangem.commands.wallet.PurgeWalletResponse import com.tangem.common.CompletionResult +import com.tangem.common.TangemSdkConstants import com.tangem.common.extensions.calculateSha256 import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tap.common.analytics.AnalyticsEvent @@ -32,13 +38,15 @@ class TangemSdkManager(val activity: ComponentActivity) { initialMessage = Message(activity.getString(R.string.initial_message_scan_header))) } - suspend fun createWallet(cardId: String?): CompletionResult { + suspend fun createWallet(cardId: String?): CompletionResult { return runTaskAsyncReturnOnMain(CreateWalletAndRescanTask(), cardId, initialMessage = Message(activity.getString(R.string.initial_message_create_wallet_body))) } suspend fun eraseWallet(cardId: String?): CompletionResult { - return runTaskAsyncReturnOnMain(PurgeWalletCommand(), cardId, + return runTaskAsyncReturnOnMain(PurgeWalletCommand( + TangemSdkConstants.getDefaultWalletIndex()), + cardId, initialMessage = Message(activity.getString(R.string.initial_message_purge_wallet_body))) } 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 497c7dde68..e2beba423d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -11,8 +11,7 @@ import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.configurable.config.ConfigManager -import com.tangem.tap.domain.extensions.amountToCreateAccount -import com.tangem.tap.domain.extensions.isNoAccountError +import com.tangem.tap.domain.extensions.* import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tokens.CardCurrencies import com.tangem.tap.domain.twins.TwinsHelper @@ -102,8 +101,10 @@ class TapWalletManager { )) } } - if (data.walletManager?.wallet?.blockchain == Blockchain.Ethereum || - data.walletManager?.wallet?.blockchain == Blockchain.EthereumTestnet) { + + val blockchain = data.card.getBlockchain() + if (blockchain == Blockchain.Ethereum || + blockchain == Blockchain.EthereumTestnet) { store.dispatch(TokensAction.LoadCardTokens) } loadData(data) @@ -112,10 +113,11 @@ class TapWalletManager { private fun updateConfigManager(data: ScanNoteResponse) { val configManager = store.state.globalState.configManager + val blockchain = data.card.getBlockchain() if (TapWorkarounds.isStart2Coin) { configManager?.turnOff(ConfigManager.isSendingToPayIdEnabled) configManager?.turnOff(ConfigManager.isTopUpEnabled) - } else if (data.walletManager?.wallet?.blockchain == Blockchain.Bitcoin + } else if (blockchain == Blockchain.Bitcoin || data.card.cardData?.blockchainName == Blockchain.Bitcoin.id) { configManager?.resetToDefault(ConfigManager.isSendingToPayIdEnabled) configManager?.resetToDefault(ConfigManager.isTopUpEnabled) @@ -128,59 +130,77 @@ class TapWalletManager { suspend fun loadData(data: ScanNoteResponse) { withContext(Dispatchers.Main) { val artworkId = data.verifyResponse?.artworkInfo?.id - if (data.walletManager != null) { - val config = store.state.globalState.configManager?.config ?: return@withContext - - val primaryWalletManager = data.walletManager - val primaryBlockchain = data.walletManager.wallet.blockchain - val primaryTokenSymbol = data.card.cardData?.tokenSymbol - val primaryToken = primaryWalletManager.presetTokens.toList() - .firstOrNull { it.symbol == primaryTokenSymbol } - - store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(primaryBlockchain)) - if (primaryToken != null) { - store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken)) + when { + data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { + store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) + store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) } - if (data.card.isMultiwalletAllowed) { - loadMultiWalletData(data.card, primaryBlockchain, primaryWalletManager) - } else { - store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) - store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) + data.card.getStatus() == CardStatus.Empty || + (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { + store.dispatch(WalletAction.EmptyWallet) + store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) + } + else -> { + val config = store.state.globalState.configManager?.config ?: return@withContext + + val blockchain = data.card.getBlockchain() + val primaryWalletManager = if (blockchain != null) { + walletManagerFactory.makeWalletManagerForApp(data.card, blockchain) + } else { + null + } + + if (blockchain != null && primaryWalletManager != null) { + val primaryToken = data.card.getToken() + + store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) + if (primaryToken != null) { + primaryWalletManager.addToken(primaryToken) + store.dispatch(WalletAction.MultiWallet.SetPrimaryToken(primaryToken)) + } + if (data.card.isMultiwalletAllowed) { + loadMultiWalletData(data.card, blockchain, primaryWalletManager) + } else { + store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) + store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(blockchain))) + } + + } else { + if (data.card.isMultiwalletAllowed) { + loadMultiWalletData(data.card, blockchain, null) + } + } + store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id)) + store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) + store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) + store.dispatch(WalletAction.LoadFiatRate()) } - store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id)) - store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) - store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) - store.dispatch(WalletAction.LoadFiatRate()) - } else if (data.card.status == CardStatus.Empty || data.card.isTwinCard()) { - store.dispatch(WalletAction.EmptyWallet) - store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) - } else { - store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) - store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) } store.dispatch(WalletAction.Warnings.CheckIfNeeded) } } private fun loadMultiWalletData( - card: Card, primaryBlockchain: Blockchain, primaryWalletManager: WalletManager + card: Card, primaryBlockchain: Blockchain?, primaryWalletManager: WalletManager? ) { - val presetTokens = primaryWalletManager.presetTokens.toList() + val presetTokens = primaryWalletManager?.presetTokens?.toList() ?: emptyList() val savedCurrencies = currenciesRepository.loadCardCurrencies(card.cardId) if (savedCurrencies == null) { - store.dispatch(WalletAction.MultiWallet.SaveCurrencies( - CardCurrencies( - blockchains = listOf(primaryBlockchain), tokens = presetTokens - ))) - store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) - store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) - store.dispatch(WalletAction.MultiWallet.AddTokens(presetTokens)) + if (primaryBlockchain != null && primaryWalletManager != null) { + store.dispatch(WalletAction.MultiWallet.SaveCurrencies( + CardCurrencies( + blockchains = listOf(primaryBlockchain), tokens = presetTokens + ))) + store.dispatch(WalletAction.MultiWallet.AddWalletManagers(primaryWalletManager)) + store.dispatch(WalletAction.MultiWallet.AddBlockchains(listOf(primaryBlockchain))) + store.dispatch(WalletAction.MultiWallet.AddTokens(presetTokens)) + } store.dispatch(WalletAction.MultiWallet.FindBlockchainsInUse(card, walletManagerFactory)) store.dispatch(WalletAction.MultiWallet.FindTokensInUse) } else { - val blockchains = listOf(primaryBlockchain) + savedCurrencies.blockchains - val walletManagers = walletManagerFactory.makeWalletManagers(card, blockchains) + val blockchains = listOfNotNull(primaryBlockchain) + savedCurrencies.blockchains + val walletManagers = walletManagerFactory.makeWalletManagersForApp(card, blockchains) val tokens = presetTokens + savedCurrencies.tokens store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManagers)) @@ -192,18 +212,23 @@ class TapWalletManager { suspend fun reloadData(data: ScanNoteResponse) { withContext(Dispatchers.Main) { - if (data.walletManager != null) { - if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { - store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) - return@withContext + when { + data.card.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { + store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) + } + data.card.getStatus() == CardStatus.Empty || + (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { + store.dispatch(WalletAction.EmptyWallet) + } + else -> { + if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { + store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) + return@withContext + } + val config = store.state.globalState.configManager?.config ?: return@withContext + store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) + store.dispatch(WalletAction.LoadFiatRate()) } - val config = store.state.globalState.configManager?.config ?: return@withContext - store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) - store.dispatch(WalletAction.LoadFiatRate()) - } else if (data.card.status == CardStatus.Empty || data.card.isTwinCard()) { - store.dispatch(WalletAction.EmptyWallet) - } else { - store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) } } } 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 eb1e716da7..afcac855d5 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -32,7 +32,7 @@ object TapWorkarounds { "0030", "0031", "0035" - ) // Tangem tags + ) private val excludedIssuers = listOf( "TTM BANK" @@ -43,5 +43,6 @@ val Card.isMultiwalletAllowed: Boolean get() { return cardData?.productMask?.contains(Product.TwinCard) != true && !TapWorkarounds.isStart2Coin - && this.curve == EllipticCurve.Secp256k1 + && (this.firmwareVersion.major >= 4 || + this.getWallets().getOrNull(0)?.curve == EllipticCurve.Secp256k1) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/BlockchainExtensions.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt similarity index 60% rename from app/src/main/java/com/tangem/tap/domain/extensions/BlockchainExtensions.kt rename to app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt index 00ad0c17ea..4f55b5b86e 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/BlockchainExtensions.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt @@ -2,6 +2,7 @@ package com.tangem.tap.domain.extensions import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.commands.common.card.EllipticCurve import org.stellar.sdk.requests.ErrorResponse import java.math.BigDecimal @@ -28,4 +29,15 @@ fun Blockchain.minimalAmount(): BigDecimal { return 1.toBigDecimal().movePointLeft(decimals()) } +fun Blockchain.getCurve(): EllipticCurve? { + return when (this) { + Blockchain.Unknown -> null + Blockchain.Bitcoin, Blockchain.BitcoinTestnet, Blockchain.BitcoinCash, Blockchain.Litecoin, + Blockchain.Ducatus, Blockchain.Ethereum, Blockchain.EthereumTestnet, Blockchain.RSK, + Blockchain.Tezos, Blockchain.XRP, Blockchain.Binance, Blockchain.BinanceTestnet -> + EllipticCurve.Secp256k1 + Blockchain.Cardano, Blockchain.CardanoShelley, Blockchain.Stellar -> EllipticCurve.Ed25519 + } +} + private const val NODL = "NODL" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt new file mode 100644 index 0000000000..4dd07f00f5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -0,0 +1,44 @@ +package com.tangem.tap.domain.extensions + +import com.tangem.FirmwareConstraints +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token +import com.tangem.commands.common.card.Card +import com.tangem.commands.common.card.CardStatus +import com.tangem.commands.wallet.CardWallet +import com.tangem.commands.wallet.WalletStatus +import com.tangem.common.TangemSdkConstants + +fun Card.getToken(): Token? { + val symbol = cardData?.tokenSymbol ?: return null + val contractAddress = cardData?.tokenContractAddress ?: return null + val decimals = cardData?.tokenDecimal ?: return null + return Token(symbol, contractAddress, decimals) +} + +fun Card.getBlockchain(): Blockchain? { + val blockchainName: String = cardData?.blockchainName ?: return null + return Blockchain.fromId(blockchainName) +} + +fun Card.getSingleWallet(): CardWallet? { + return wallet(TangemSdkConstants.getDefaultWalletIndex()) +} + +fun Card.getStatus(): CardStatus { + if (firmwareVersion < FirmwareConstraints.AvailabilityVersions.walletData) return status!! + + return if (getWallets().any { it.status == WalletStatus.Loaded }) { + CardStatus.Loaded + } else { + CardStatus.Empty + } +} + +fun Card.hasSignedHashes(): Boolean { + return getWallets().any { it.status == WalletStatus.Loaded && it.signedHashes ?: 0 > 0 } +} + +fun Card.signedHashesCount(): Int { + return getWallets().map { it.signedHashes ?: 0 }.sum() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt b/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt new file mode 100644 index 0000000000..c8710e3aeb --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/extensions/TangemSdkConstants.kt @@ -0,0 +1,8 @@ +package com.tangem.tap.domain.extensions + +import com.tangem.commands.wallet.WalletIndex +import com.tangem.common.TangemSdkConstants + +fun TangemSdkConstants.Companion.getDefaultWalletIndex(): WalletIndex { + return WalletIndex.Index(oldCardDefaultWalletIndex) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt new file mode 100644 index 0000000000..fc847ae987 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt @@ -0,0 +1,33 @@ +package com.tangem.tap.domain.extensions + +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.commands.common.card.Card +import com.tangem.commands.common.card.EllipticCurve + +fun WalletManagerFactory.makeWalletManagerForApp(card: Card, blockchain: Blockchain): WalletManager? { + val curve = blockchain.getCurve() ?: return null + val publicKey = card.getWallets().firstOrNull { it.curve == curve }?.publicKey ?: return null + return makeWalletManager(card.cardId, publicKey, blockchain, curve) +} + +fun WalletManagerFactory.makeWalletManagersForApp( + card: Card, blockchains: List +): List { + return makeWalletManagersForCurve(card, blockchains, EllipticCurve.Secp256k1) + + makeWalletManagersForCurve(card, blockchains, EllipticCurve.Ed25519) +} + +fun WalletManagerFactory.makeWalletManagersForCurve( + card: Card, blockchains: List, curve: EllipticCurve +): List { + val blockchainsForCurve = blockchains.filter { it.getCurve() == curve } + val walletPublicKey = card.getWallets().firstOrNull { it.curve == curve }?.publicKey + + return if (walletPublicKey != null) { + makeWalletManagers(card.cardId, walletPublicKey, blockchainsForCurve, curve) + } else { + emptyList() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt index 0af8e1f29b..735cc1de64 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt @@ -2,26 +2,39 @@ package com.tangem.tap.domain.tasks import com.tangem.CardSession import com.tangem.CardSessionRunnable -import com.tangem.commands.ReadCommand +import com.tangem.FirmwareConstraints +import com.tangem.TangemSdkError +import com.tangem.commands.common.card.Card import com.tangem.common.CompletionResult import com.tangem.tasks.CreateWalletTask +import com.tangem.tasks.PreflightReadCapable +import com.tangem.tasks.PreflightReadSettings +import com.tangem.tasks.PreflightReadTask -class CreateWalletAndRescanTask : CardSessionRunnable { +class CreateWalletAndRescanTask : CardSessionRunnable, PreflightReadCapable { override val requiresPin2 = false + override fun preflightReadSettings() = PreflightReadSettings.FullCardRead - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - CreateWalletTask().run(session) { result -> + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val firmwareVerion = session.environment.card?.firmwareVersion + if (firmwareVerion == null) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + val task = if (firmwareVerion < FirmwareConstraints.AvailabilityVersions.walletData) { + CreateWalletTask() + } else { + CreateWalletsTask() + } + + task.run(session) { result -> when (result) { - is CompletionResult.Success -> { - ReadCommand().run(session) { readResult -> - when (readResult) { - is CompletionResult.Success -> ScanNoteTask(readResult.data).run(session, callback) - is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) - } - } - } + is CompletionResult.Success -> + PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } + + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt new file mode 100644 index 0000000000..b72cf26ae3 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt @@ -0,0 +1,64 @@ +package com.tangem.tap.domain.tasks + +import com.tangem.CardSession +import com.tangem.CardSessionRunnable +import com.tangem.commands.common.card.Card +import com.tangem.commands.common.card.EllipticCurve +import com.tangem.commands.wallet.CreateWalletCommand +import com.tangem.commands.wallet.WalletConfig +import com.tangem.common.CompletionResult +import com.tangem.tasks.PreflightReadSettings +import com.tangem.tasks.PreflightReadTask + +class CreateWalletsTask(wallets: List = emptyList()) : CardSessionRunnable { + override val requiresPin2 = false + + val wallets = if (wallets.isEmpty()) { + listOf( + WalletConfig(null, null, EllipticCurve.Secp256k1, null), + WalletConfig(null, null, EllipticCurve.Ed25519, null), + WalletConfig(null, null, EllipticCurve.Secp256r1, null), + ) + } else { + wallets + } + + var index = 0 + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val walletConfig = wallets[index] + createWallet(walletConfig, session, callback) + } + + private fun createWallet( + walletConfig: WalletConfig, session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + + CreateWalletCommand( + walletConfig, walletIndexValue = walletConfig.curveId!!.toWalletIndex() + ).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + if (index == wallets.lastIndex) { + PreflightReadTask(PreflightReadSettings.FullCardRead).run(session, callback) + return@run + } + index += 1 + createWallet(wallets[index], session, callback) + } + is CompletionResult.Failure -> { + callback(CompletionResult.Failure(result.error)) + } + } + } + } +} + +private fun EllipticCurve.toWalletIndex(): Int { + return when (this) { + EllipticCurve.Secp256k1 -> 0 + EllipticCurve.Ed25519 -> 1 + EllipticCurve.Secp256r1 -> 2 + } +} \ No newline at end of file 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 0c6e990dc3..f252714774 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 @@ -1,36 +1,40 @@ package com.tangem.tap.domain.tasks import com.tangem.* -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.commands.CommandResponse import com.tangem.commands.ReadIssuerDataCommand import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.CardStatus -import com.tangem.commands.verifycard.VerifyCardCommand -import com.tangem.commands.verifycard.VerifyCardResponse +import com.tangem.commands.common.card.EllipticCurve +import com.tangem.commands.verification.VerifyCardCommand +import com.tangem.commands.verification.VerifyCardResponse +import com.tangem.commands.wallet.WalletConfig import com.tangem.common.CompletionResult import com.tangem.common.extensions.toHexString import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.TapWorkarounds.isExcluded -import com.tangem.tap.domain.isMultiwalletAllowed +import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.extensions.getStatus import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.domain.twins.isTwinCard import com.tangem.tap.store +import com.tangem.tasks.PreflightReadCapable +import com.tangem.tasks.PreflightReadSettings import com.tangem.tasks.ScanTask data class ScanNoteResponse( - val walletManager: WalletManager?, val card: Card, val verifyResponse: VerifyCardResponse? = null, val secondTwinPublicKey: String? = null, ) : CommandResponse -class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { +class ScanNoteTask(val card: Card? = null) : CardSessionRunnable, PreflightReadCapable { override val requiresPin2 = false + override fun preflightReadSettings() = PreflightReadSettings.FullCardRead + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { ScanTask().run(session) { result -> when (result) { @@ -48,39 +52,25 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit + card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit ) { - VerifyCardCommand(true).run(session) { verifyResult -> when (verifyResult) { is CompletionResult.Success -> { - callback(CompletionResult.Success(ScanNoteResponse( - walletManager, card, verifyResult.data, publicKey))) + if (card.isTwinCard()) { + dealWithTwinCard(card, session, verifyResult.data, callback) + } else if (card.firmwareVersion.major >= 4) { + createMissingWalletsIfNeeded(card, session, verifyResult.data, callback) + } else { + callback(CompletionResult.Success(ScanNoteResponse(card, verifyResult.data))) + } } is CompletionResult.Failure -> { callback(CompletionResult.Failure(TangemSdkError.CardVerificationFailed())) @@ -89,39 +79,73 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit + ) { + if (card.getStatus() == CardStatus.Empty) { + callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse))) + return + } + + val curvesPresent = card.getWallets().map { it.curve } + val curvesToCreate = EllipticCurve.values().subtract(curvesPresent) + + if (curvesToCreate.isEmpty()) { + callback(CompletionResult.Success(ScanNoteResponse(card, verifyResponse))) + return + } + + val configs = curvesToCreate.map { curve -> + WalletConfig( + isReusable = null, prohibitPurgeWallet = null, curveId = curve, + signingMethods = null + ) + } + CreateWalletsTask(configs).run(session) { result -> + when (result) { + is CompletionResult.Success -> + callback(CompletionResult.Success(ScanNoteResponse(result.data, verifyResponse))) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + + } + private fun dealWithTwinCard( - card: Card, session: CardSession, + card: Card, session: CardSession, verifyResponse: VerifyCardResponse, callback: (result: CompletionResult) -> Unit ) { ReadIssuerDataCommand().run(session) { readDataResult -> when (readDataResult) { is CompletionResult.Success -> { + val publicKey = card.getSingleWallet()?.publicKey + if (publicKey == null) { + callback(CompletionResult.Success(ScanNoteResponse(card, null))) + return@run + } val verified = TwinCardsManager.verifyTwinPublicKey( - readDataResult.data.issuerData, card.walletPublicKey + readDataResult.data.issuerData, publicKey ) if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) - val walletManager = try { - getWalletManagerFactory().makeMultisigWalletManager(card, twinPublicKey) - } catch (exception: Exception) { - callback(CompletionResult.Success(ScanNoteResponse(null, card))) - return@run - } - verifyCard(walletManager, card, twinPublicKey.toHexString(), session, callback) + callback(CompletionResult.Success( + ScanNoteResponse(card, verifyResponse, twinPublicKey.toHexString()) + )) return@run } else { - callback(CompletionResult.Success(ScanNoteResponse(null, card))) + callback(CompletionResult.Success(ScanNoteResponse(card, null))) } } is CompletionResult.Failure -> - callback(CompletionResult.Success(ScanNoteResponse(null, card))) + callback(CompletionResult.Success(ScanNoteResponse(card, null))) } } } private fun getErrorIfExcludedCard(card: Card): TangemError? { if (card.isExcluded()) return TapSdkError.CardForDifferentApp - if (card.status == CardStatus.Purged) return TangemSdkError.CardIsPurged() + if (card.status == CardStatus.Purged) return TangemSdkError.WalletIsPurged() if (card.status == CardStatus.NotPersonalized) return TangemSdkError.NotPersonalized() return null } diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt index 42c7aeefe4..78afe93215 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt @@ -7,6 +7,7 @@ import com.squareup.moshi.JsonClass import com.squareup.moshi.Types import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.commands.common.card.FirmwareVersion import com.tangem.tap.common.extensions.readJsonFileToString import com.tangem.tap.network.createMoshi @@ -93,19 +94,27 @@ class CurrenciesRepository(val context: Application) { return tokensAdapter.fromJson(json)!!.map { it.toToken() } } - fun getBlockchains(): List { - return listOf( - Blockchain.Bitcoin, Blockchain.BitcoinCash, Blockchain.Binance, Blockchain.Litecoin, - Blockchain.XRP, Blockchain.Tezos, - Blockchain.Ethereum, Blockchain.RSK) + fun getBlockchains(cardFirmware: FirmwareVersion?): List { + return if (cardFirmware == null || cardFirmware.major < 4) { + secp256k1Blochcains + } else { + secp256k1Blochcains + ed25519Blockchains + } } companion object { private const val POPULAR_TOKENS_FILE_NAME = "erc20_tokens" private const val FILE_NAME_PREFIX_TOKENS = "tokens" private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" + fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId" fun getFileNameForBlockchains(cardId: String): String = "${FILE_NAME_PREFIX_BLOCKCHAINS}_$cardId" + + private val secp256k1Blochcains = listOf( + Blockchain.Bitcoin, Blockchain.BitcoinCash, Blockchain.Binance, Blockchain.Litecoin, + Blockchain.XRP, Blockchain.Tezos, + Blockchain.Ethereum, Blockchain.RSK) + private val ed25519Blockchains = listOf(Blockchain.CardanoShelley, Blockchain.Stellar) } } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt index 7296c72a11..0b424bb64d 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt @@ -2,18 +2,20 @@ package com.tangem.tap.domain.twins import com.tangem.CardSession import com.tangem.CardSessionRunnable -import com.tangem.commands.CreateWalletResponse -import com.tangem.commands.PurgeWalletCommand import com.tangem.commands.common.card.CardStatus +import com.tangem.commands.wallet.CreateWalletResponse +import com.tangem.commands.wallet.PurgeWalletCommand import com.tangem.common.CompletionResult +import com.tangem.common.TangemSdkConstants +import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tasks.CreateWalletTask class CreateFirstTwinWalletTask : CardSessionRunnable { override val requiresPin2 = false override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - if (session.environment.card?.walletPublicKey != null) { - PurgeWalletCommand().run(session) { response -> + if (session.environment.card?.getSingleWallet()?.publicKey != null) { + PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response -> when (response) { is CompletionResult.Success -> { session.environment.card = session.environment.card?.copy(status = CardStatus.Empty) diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index 5587faa1ab..e02a2511c2 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -3,11 +3,13 @@ package com.tangem.tap.domain.twins import com.tangem.CardSession import com.tangem.CardSessionRunnable import com.tangem.Message -import com.tangem.commands.CreateWalletResponse -import com.tangem.commands.PurgeWalletCommand import com.tangem.commands.common.card.CardStatus +import com.tangem.commands.wallet.CreateWalletResponse +import com.tangem.commands.wallet.PurgeWalletCommand import com.tangem.common.CompletionResult +import com.tangem.common.TangemSdkConstants import com.tangem.common.extensions.hexToBytes +import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tasks.CreateWalletTask class CreateSecondTwinWalletTask( @@ -18,9 +20,9 @@ class CreateSecondTwinWalletTask( override val requiresPin2 = true override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - if (session.environment.card?.walletPublicKey != null) { + if (session.environment.card?.getSingleWallet()?.publicKey != null) { session.setInitialMessage(preparingMessage) - PurgeWalletCommand().run(session) { response -> + PurgeWalletCommand(TangemSdkConstants.getDefaultWalletIndex()).run(session) { response -> when (response) { is CompletionResult.Success -> { session.environment.card = diff --git a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt index 073c47b344..e8454b6756 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt @@ -3,7 +3,7 @@ package com.tangem.tap.domain.twins import com.tangem.CardSession import com.tangem.CardSessionRunnable import com.tangem.KeyPair -import com.tangem.commands.ReadCommand +import com.tangem.commands.read.ReadCommand import com.tangem.common.CompletionResult import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tasks.ScanNoteTask diff --git a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt index 30d3b688dd..5ab77f557e 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt @@ -6,6 +6,7 @@ import com.tangem.KeyPair import com.tangem.TangemSdkError import com.tangem.commands.* import com.tangem.common.CompletionResult +import com.tangem.common.TangemSdkConstants import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.guard import com.tangem.common.files.FileHashHelper @@ -17,14 +18,14 @@ class WriteProtectedIssuerDataTask( override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val hashes = arrayOf(twinPublicKey.calculateSha256()) - SignCommand(hashes).run(session) { signResult -> + SignCommand(hashes, TangemSdkConstants.getDefaultWalletIndex()).run(session) { signResult -> when (signResult) { is CompletionResult.Success -> { ReadIssuerDataCommand().run(session) { readResult -> when (readResult) { is CompletionResult.Success -> { writeIssuerData( - twinPublicKey, issuerKeys, signResult.data.signature, + twinPublicKey, issuerKeys, signResult.data.signatures[0], readResult.data, session, callback ) } 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 ee44e36c9d..4ec1681b66 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 @@ -203,6 +203,5 @@ private fun prepareAllowedSecurityOptions(card: Card): EnumSet { private fun Card.toCardInfo(): CardInfo? { val cardId = this.cardId.chunked(4).joinToString(separator = " ") val issuer = this.cardData?.issuerName ?: return null - val signedHashes = this.walletSignedHashes ?: return null - return CardInfo(cardId, issuer, signedHashes) + return CardInfo(cardId, issuer) } \ No newline at end of file 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 5ef1033e2f..22fca3db45 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 @@ -26,7 +26,6 @@ data class DetailsState( data class CardInfo( val cardId: String, val issuer: String, - val signedHashes: Int, ) enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty } 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 669ab9be91..e99f7c7ad6 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.globalState.scanNoteResponse?.walletManager?.wallet + val wallet = store.state.detailsState.wallets.firstOrNull() 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 895f379b09..fc793be4cc 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 @@ -74,12 +74,8 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber) { @@ -274,7 +275,6 @@ class FeedbackEmail : EmailData { val builder = StringBuilder() builder.appendKeyValue("Card ID", infoHolder.cardId) builder.appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion) - builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount) infoHolder.walletsInfo.forEach { builder.appendKeyValue("Blockchain", it.blockchain.fullName) 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 5a5f927b30..a28d92df4b 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 @@ -4,14 +4,13 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder import com.tangem.blockchain.common.* -import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.Signer +import com.tangem.blockchain.extensions.SimpleResult import com.tangem.commands.common.card.Card import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapWorkarounds @@ -115,11 +114,10 @@ private fun sendTransaction( val result = (walletManager as TransactionSender).send(txData, signer) withContext(Dispatchers.Main) { when (result) { - is Result.Success -> { + is SimpleResult.Success -> { tangemSdk.config.linkedTerminal = isLinkedTerminal FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.TRANSACTION_IS_SENT, card) dispatch(SendAction.SendSuccess) - dispatch(GlobalAction.UpdateWalletSignedHashes(result.data.walletSignedHashes)) dispatch(NavigationAction.PopBackTo()) scope.launch(Dispatchers.IO) { withContext(Dispatchers.Main) { @@ -131,7 +129,7 @@ private fun sendTransaction( } } } - is Result.Failure -> { + is SimpleResult.Failure -> { when (result.error) { is CreateAccountUnderfunded -> { val error = result.error as CreateAccountUnderfunded 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 9db3d6e7ca..8cad24d39c 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 @@ -5,7 +5,6 @@ import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.states.IdStateHolder import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.store import org.rekotlin.Action import java.math.BigDecimal @@ -60,8 +59,7 @@ private class EmptyReducer : SendInternalReducer { private class PrepareSendScreenStatesReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState { val prepareAction = action as PrepareSendScreen - val walletManager = action.walletManager - ?: store.state.globalState.scanNoteResponse!!.walletManager!! + val walletManager = action.walletManager!! val amountToExtract = prepareAction.tokenAmount ?: prepareAction.coinAmount!! val decimals = amountToExtract.decimals diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index 7923d17bd4..cb13d64dd0 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -13,8 +13,9 @@ class TokensMiddleware { { action -> when (action) { is TokensAction.LoadCurrencies -> { + val cardFirmware = state()?.globalState?.scanNoteResponse?.card?.firmwareVersion val tokens = currenciesRepository.getPopularTokens() - val blockchains = currenciesRepository.getBlockchains() + val blockchains = currenciesRepository.getBlockchains(cardFirmware) val currencies = CurrencyListItem.createListOfCurrencies( blockchains, tokens ) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 6daa182e35..d68d7863f2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -10,6 +10,8 @@ import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository +import com.tangem.tap.domain.extensions.makeWalletManagerForApp +import com.tangem.tap.domain.extensions.makeWalletManagersForApp import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.scope @@ -46,7 +48,7 @@ class MultiWalletMiddleware { globalState?.scanNoteResponse?.card?.let { card -> currenciesRepository.saveAddedBlockchain(card.cardId, action.blockchain) globalState.tapWalletManager.walletManagerFactory - .makeWalletManager(card, action.blockchain)?.let { + .makeWalletManagerForApp(card, action.blockchain)?.let { store.dispatch(WalletAction.MultiWallet.AddWalletManagers(it)) } } @@ -66,9 +68,10 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.FindBlockchainsInUse -> { - val blockchains = currenciesRepository.getBlockchains() + val cardFirmware = globalState?.scanNoteResponse?.card?.firmwareVersion + val blockchains = currenciesRepository.getBlockchains(cardFirmware) .filterNot { walletState?.blockchains?.contains(it) == true } - val walletManagers = action.factory.makeWalletManagers(action.card, blockchains) + val walletManagers = action.factory.makeWalletManagersForApp(action.card, blockchains) scope.launch { walletManagers.map { walletManager -> diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 05993a08ef..e11a577a51 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -108,7 +108,11 @@ class WalletMiddleware { ) when (result) { is CompletionResult.Success -> { - globalState?.tapWalletManager?.onCardScanned(result.data) + val scanNoteResponse = + globalState?.scanNoteResponse?.copy(card = result.data) + scanNoteResponse?.let { + globalState.tapWalletManager.onCardScanned(scanNoteResponse) + } } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index d158d3d323..79ab8e3feb 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -6,7 +6,7 @@ import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.extensions.SimpleResult import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.CardType -import com.tangem.commands.verifycard.VerifyCardState +import com.tangem.commands.verification.VerifyCardState import com.tangem.common.extensions.getType import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler @@ -14,6 +14,8 @@ import com.tangem.tap.common.extensions.isGreaterThan import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager +import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.extensions.hasSignedHashes import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.twins.isTwinCard import com.tangem.tap.features.wallet.redux.WalletAction @@ -63,13 +65,12 @@ class WarningsMiddleware { } private fun showCardWarningsIfNeeded(globalState: GlobalState?) { - val validator = globalState?.scanNoteResponse?.walletManager as? SignatureCountValidator globalState?.scanNoteResponse?.card?.let { card -> globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local) if (card.getType() != CardType.Release) { addWarningMessage(WarningMessagesManager.devCardWarning()) } else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) { - checkIfWarningNeeded(card, validator)?.let { addWarningMessage(it) } + checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) } } if (card.getType() == CardType.Release) { if (globalState.scanNoteResponse.verifyResponse?.verificationState == @@ -82,12 +83,14 @@ class WarningsMiddleware { } private fun checkIfWarningNeeded( - card: Card, signatureCountValidator: SignatureCountValidator? = null, + card: Card ): WarningMessage? { - if (card.isTwinCard() || card.isMultiwalletAllowed) return null + if (card.isTwinCard()) return null - return if (signatureCountValidator == null) { - if (card.walletSignedHashes ?: 0 > 0) { + val validator = store.state.walletState.walletManagers.firstOrNull() + as? SignatureCountValidator + return if (validator == null || card.isMultiwalletAllowed) { + if (card.hasSignedHashes()) { WarningMessagesManager.alreadySignedHashesWarning() } else { store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) @@ -108,10 +111,11 @@ class WarningsMiddleware { if (card.isTwinCard() || card.isMultiwalletAllowed) return - val validator = store.state.globalState.scanNoteResponse?.walletManager + val validator = store.state.walletState.walletManagers.firstOrNull() as? SignatureCountValidator scope.launch { - val result = validator?.validateSignatureCount(card.walletSignedHashes ?: 0) + val signedHashes = card.getSingleWallet()?.signedHashes ?: 0 + val result = validator?.validateSignatureCount(signedHashes) withContext(Dispatchers.Main) { when (result) { SimpleResult.Success -> { @@ -121,7 +125,7 @@ class WarningsMiddleware { is SimpleResult.Failure -> if (result.error is BlockchainSdkError.SignatureCountNotMatched) { addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true) - } else if (card.walletSignedHashes ?: 0 > 0) { + } else if (signedHashes > 0) { addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index 9e2320d30c..6e69a80b7d 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -34,28 +34,26 @@ class MultiWalletView : WalletView { override fun changeWalletView(fragment: WalletFragment) { setFragment(fragment) onViewCreated() - showMultiWalletView() - setupButtons() + showMultiWalletView(fragment) + setupButtons(fragment) } - private fun showMultiWalletView() { - val fragment = fragment ?: return - fragment.tv_twin_card_number.hide() - fragment.iv_twin_card.hide() - fragment.rv_pending_transaction.hide() - fragment.l_card_balance.hide() - fragment.l_address.hide() - fragment.l_buttons_short.hide() - fragment.l_buttons_long.hide() - fragment.btn_scan_multiwallet?.show() - fragment.rv_multiwallet.show() - fragment.btn_add_token.show() + private fun showMultiWalletView(fragment: WalletFragment) = with(fragment) { + tv_twin_card_number.hide() + iv_twin_card.hide() + rv_pending_transaction.hide() + l_card_balance.hide() + l_address.hide() + l_buttons_short.hide() + l_buttons_long.hide() + btn_scan_multiwallet?.show() + rv_multiwallet.show() + btn_add_token.show() } - private fun setupButtons() { - val fragment = fragment ?: return - fragment.btn_scan_multiwallet?.setOnClickListener { store.dispatch(WalletAction.Scan) } + private fun setupButtons(fragment: WalletFragment) = with(fragment) { + btn_scan_multiwallet?.setOnClickListener { store.dispatch(WalletAction.Scan) } } override fun setFragment(fragment: WalletFragment) { @@ -114,22 +112,22 @@ class MultiWalletView : WalletView { private fun showErrorState( fragment: WalletFragment, errorTitle: CharSequence, errorDescription: CharSequence, - ) { - fragment.l_card_balance.show() - fragment.l_balance.hide() - fragment.l_balance_error.show() - fragment.rv_multiwallet.hide() - fragment.btn_add_token.hide() - fragment.tv_error_title.text = errorTitle - fragment.tv_error_descriptions.text = errorDescription + ) = with(fragment) { + l_card_balance.show() + l_balance.hide() + l_balance_error.show() + rv_multiwallet.hide() + btn_add_token.hide() + tv_error_title.text = errorTitle + tv_error_descriptions.text = errorDescription } - private fun configureButtonsForEmptyWalletState(fragment: WalletFragment) { - fragment.btn_scan_multiwallet.hide() - fragment.l_buttons_long.show() - fragment.btn_scan_long.setOnClickListener { store.dispatch(WalletAction.Scan) } - fragment.btn_confirm_long.setOnClickListener { store.dispatch(WalletAction.CreateWallet) } - fragment.btn_confirm_long.text = fragment.getText(R.string.wallet_button_create_wallet) + private fun configureButtonsForEmptyWalletState(fragment: WalletFragment) = with(fragment) { + btn_scan_multiwallet.hide() + l_buttons_long.show() + btn_scan_long.setOnClickListener { store.dispatch(WalletAction.Scan) } + btn_confirm_long.setOnClickListener { store.dispatch(WalletAction.CreateWallet) } + btn_confirm_long.text = fragment.getText(R.string.wallet_button_create_wallet) } private fun handleDialogs(walletDialog: StateDialog?) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index d6762f815b..97f2618542 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -44,17 +44,16 @@ class SingleWalletView : WalletView { override fun changeWalletView(fragment: WalletFragment) { setFragment(fragment) onViewCreated() - showSingleWalletView() + showSingleWalletView(fragment) } - private fun showSingleWalletView() { - val fragment = fragment ?: return - fragment.rv_multiwallet.hide() - fragment.btn_add_token.hide() - fragment.btn_scan_multiwallet?.hide() - fragment.rv_pending_transaction.hide() - fragment.l_card_balance.show() - fragment.l_address.show() + private fun showSingleWalletView(fragment: WalletFragment) = with(fragment) { + rv_multiwallet.hide() + btn_add_token.hide() + btn_scan_multiwallet?.hide() + rv_pending_transaction.hide() + l_card_balance.show() + l_address.show() } override fun onViewCreated() { @@ -70,13 +69,15 @@ class SingleWalletView : WalletView { } override fun onNewState(state: WalletState) { - setupTwinCards(state.twinCardsState) + val fragment = fragment ?: return state.primaryWallet ?: return - setupButtons(state.primaryWallet, state.twinCardsState != null) - setupAddressCard(state.primaryWallet) + + setupTwinCards(state.twinCardsState, fragment) + setupButtons(state.primaryWallet, state.twinCardsState != null, fragment) + setupAddressCard(state.primaryWallet, fragment) showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions) setupBalance(state, state.primaryWallet) - handleDialogs(state.walletDialog) + handleDialogs(state.walletDialog, fragment) } private fun showPendingTransactionsIfPresent(pendingTransactions: List) { @@ -92,43 +93,45 @@ class SingleWalletView : WalletView { } } - private fun setupTwinCards(twinCardsState: TwinCardsState?) { - fragment?.apply { - twinCardsState?.cardNumber?.let { cardNumber -> - this.tv_twin_card_number.show() - this.iv_twin_card.show() - val number = when (cardNumber) { - TwinCardNumber.First -> "1" - TwinCardNumber.Second -> "2" - } - this.tv_twin_card_number.text = - this.getString(R.string.wallet_twins_chip_format, number) - } - if (twinCardsState?.cardNumber == null) { - this.tv_twin_card_number.hide() - this.iv_twin_card.hide() - } - if (twinCardsState?.showTwinOnboarding == true) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) + private fun setupTwinCards( + twinCardsState: TwinCardsState?, fragment: WalletFragment + ) = with(fragment) { + twinCardsState?.cardNumber?.let { cardNumber -> + tv_twin_card_number.show() + iv_twin_card.show() + val number = when (cardNumber) { + TwinCardNumber.First -> "1" + TwinCardNumber.Second -> "2" } + tv_twin_card_number.text = + this.getString(R.string.wallet_twins_chip_format, number) } + if (twinCardsState?.cardNumber == null) { + tv_twin_card_number.hide() + iv_twin_card.hide() + } + if (twinCardsState?.showTwinOnboarding == true) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) + } + } - private fun setupButtons(state: WalletData, isTwinsWallet: Boolean) { - val fragment = fragment ?: return + private fun setupButtons( + state: WalletData, isTwinsWallet: Boolean, fragment: WalletFragment + ) = with(fragment){ setupButtonsType(state, fragment) val btnConfirm = if (state.topUpState.allowed) { - fragment.btn_confirm_short + btn_confirm_short } else { - fragment.btn_confirm_long + btn_confirm_long } val btnScan = if (state.topUpState.allowed) { - fragment.btn_scan_short + btn_scan_short } else { - fragment.btn_scan_long + btn_scan_long } setupConfirmButton(state, btnConfirm, fragment, isTwinsWallet) @@ -137,27 +140,27 @@ class SingleWalletView : WalletView { store.dispatch(WalletAction.Scan) } - fragment.btn_copy.setOnClickListener { + btn_copy.setOnClickListener { state.walletAddresses?.selectedAddress?.address?.let { addressString -> store.dispatch(WalletAction.CopyAddress(addressString, fragment.requireContext())) } } - fragment.btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } + btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } - fragment.btn_top_up.setOnClickListener { + btn_top_up.setOnClickListener { store.dispatch( WalletAction.TopUpAction.TopUp(fragment.requireContext(), R.color.backgroundLightGray) ) } } - private fun setupButtonsType(state: WalletData, fragment: WalletFragment) { + private fun setupButtonsType(state: WalletData, fragment: WalletFragment) = with(fragment) { if (state.topUpState.allowed) { - fragment.l_buttons_long.hide() - fragment.l_buttons_short.show() + l_buttons_long.hide() + l_buttons_short.show() } else { - fragment.l_buttons_long.show() - fragment.l_buttons_short.hide() + l_buttons_long.show() + l_buttons_short.hide() } } @@ -185,39 +188,37 @@ class SingleWalletView : WalletView { } - private fun setupAddressCard(state: WalletData) { - val fragment = fragment ?: return + private fun setupAddressCard(state: WalletData, fragment: WalletFragment) = with(fragment) { if (state.walletAddresses != null && state.blockchain != null) { - fragment.l_address?.show() + l_address?.show() if (state.shouldShowMultipleAddress()) { - (fragment.l_address as? ViewGroup)?.beginDelayedTransition() - fragment.chip_group_address_type.show() - fragment.chip_group_address_type.fitChipsByGroupWidth() + (l_address as? ViewGroup)?.beginDelayedTransition() + chip_group_address_type.show() + chip_group_address_type.fitChipsByGroupWidth() val checkedId = MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type) - if (checkedId != View.NO_ID) fragment.chip_group_address_type.check(checkedId) + if (checkedId != View.NO_ID) chip_group_address_type.check(checkedId) - fragment.chip_group_address_type.setOnCheckedChangeListener { group, checkedId -> + chip_group_address_type.setOnCheckedChangeListener { group, checkedId -> if (checkedId == -1) return@setOnCheckedChangeListener val type = MultipleAddressUiHelper.idToType(checkedId, state.blockchain) type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) } } } else { - fragment.chip_group_address_type.hide() + chip_group_address_type.hide() } - fragment.tv_address.text = state.walletAddresses.selectedAddress.address - fragment.tv_explore?.setOnClickListener { + tv_address.text = state.walletAddresses.selectedAddress.address + tv_explore?.setOnClickListener { store.dispatch(WalletAction.ExploreAddress( state.walletAddresses.selectedAddress.exploreUrl, fragment.requireContext())) } } else { - fragment.l_address?.hide() + l_address?.hide() } } - private fun handleDialogs(walletDialog: StateDialog?) { - val fragment = fragment ?: return + private fun handleDialogs(walletDialog: StateDialog?, fragment: WalletFragment) { val context = fragment.context ?: return when (walletDialog) { is WalletDialog.QrDialog -> { diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 00c0626cba..69b8b5cf55 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -90,28 +90,6 @@ app:layout_constraintTop_toBottomOf="@id/tv_card_id" tools:text="Tangem" /> - - - - + app:layout_constraintTop_toBottomOf="@id/tv_issuer_title" /> Date: Thu, 22 Apr 2021 15:14:40 +0300 Subject: [PATCH 08/21] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt | 1 + .../com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt | 1 + .../com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt | 1 + .../com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt | 1 + 4 files changed, 4 insertions(+) diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index e6524859c6..d4ce5e4b9f 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -16,6 +16,7 @@ import com.tangem.common.extensions.calculateSha256 import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.AnalyticsHandler +import com.tangem.tap.domain.extensions.getDefaultWalletIndex import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tasks.ScanNoteTask diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt index 0b424bb64d..e9b5dd4435 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateFirstTwinWalletTask.kt @@ -7,6 +7,7 @@ import com.tangem.commands.wallet.CreateWalletResponse import com.tangem.commands.wallet.PurgeWalletCommand import com.tangem.common.CompletionResult import com.tangem.common.TangemSdkConstants +import com.tangem.tap.domain.extensions.getDefaultWalletIndex import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tasks.CreateWalletTask diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index e02a2511c2..9946e37d07 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -9,6 +9,7 @@ import com.tangem.commands.wallet.PurgeWalletCommand import com.tangem.common.CompletionResult import com.tangem.common.TangemSdkConstants import com.tangem.common.extensions.hexToBytes +import com.tangem.tap.domain.extensions.getDefaultWalletIndex import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tasks.CreateWalletTask diff --git a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt index 5ab77f557e..bf6c735be5 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/WriteProtectedIssuerDataTask.kt @@ -10,6 +10,7 @@ import com.tangem.common.TangemSdkConstants import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.guard import com.tangem.common.files.FileHashHelper +import com.tangem.tap.domain.extensions.getDefaultWalletIndex class WriteProtectedIssuerDataTask( private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair From 00730835595d7b64a7cb2db76c4174cffc0681e5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 26 Apr 2021 10:20:08 +0300 Subject: [PATCH 09/21] Updated on 2026-08-14 --- app/src/main/assets/tangem-android-config | 1 - app/src/main/assets/tangem-app-config | 1 + app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 160000 app/src/main/assets/tangem-android-config create mode 160000 app/src/main/assets/tangem-app-config diff --git a/app/src/main/assets/tangem-android-config b/app/src/main/assets/tangem-android-config deleted file mode 160000 index cbdda0d566..0000000000 --- a/app/src/main/assets/tangem-android-config +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cbdda0d566da0dd1baa534fd5ce815ce7c88cea0 diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config new file mode 160000 index 0000000000..415eb6e9cc --- /dev/null +++ b/app/src/main/assets/tangem-app-config @@ -0,0 +1 @@ +Subproject commit 415eb6e9ccfcdc3573391b869adeba433f790087 diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt b/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt index 3471ed78ee..873b71c500 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/Loader.kt @@ -10,7 +10,7 @@ interface Loader { companion object { const val featuresName = "features_${BuildConfig.CONFIG_ENVIRONMENT}" - const val configValuesName = "tangem-android-config/config_${BuildConfig.CONFIG_ENVIRONMENT}" + const val configValuesName = "tangem-app-config/config_${BuildConfig.CONFIG_ENVIRONMENT}" const val warnings = "warnings_${BuildConfig.CONFIG_ENVIRONMENT}" } } \ No newline at end of file From 6a7a869eccc026e4b9398d4633a8e87db14a6a8f Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 28 Apr 2021 23:10:01 +0300 Subject: [PATCH 10/21] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 e2beba423d..c8995c0f97 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -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) } } From 5e770b26b56313ea10865d30cc3a9d9f4fadf0f5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 5 May 2021 18:28:22 +0300 Subject: [PATCH 11/21] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/network/NetworkConnectivity.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/tangem/tap/network/NetworkConnectivity.kt b/app/src/main/java/com/tangem/tap/network/NetworkConnectivity.kt index 05c2b6a6bd..00c2f87e19 100644 --- a/app/src/main/java/com/tangem/tap/network/NetworkConnectivity.kt +++ b/app/src/main/java/com/tangem/tap/network/NetworkConnectivity.kt @@ -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 { From cf785b1f75dba4da2a1a83784b4e237a1afcd55e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 6 May 2021 07:23:13 +0300 Subject: [PATCH 12/21] Updated on 2026-08-14 --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0fde302b00..13b2127afa 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ # Local configuration file (sdk path, etc) local.properties +# Google services + + # Gradle generated files .gradle From 23daa7bed70c77f12786a0ed12bfe3653a1209db Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 12 May 2021 10:50:06 +0300 Subject: [PATCH 13/21] Updated on 2026-08-14 --- .../java/com/tangem/tap/features/details/redux/DetailsReducer.kt | 1 + 1 file changed, 1 insertion(+) 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 4ec1681b66..5ec4fe3547 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 @@ -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() } From 944a01ba42706444e491aeb9cad42057f57d9070 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 12 May 2021 10:53:40 +0300 Subject: [PATCH 14/21] Updated on 2026-08-14 --- .../tap/features/wallet/redux/WalletState.kt | 3 +++ .../redux/middlewares/MultiWalletMiddleware.kt | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) 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 244f7f026d..0dabc601c8 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 @@ -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() && diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index d68d7863f2..da28809df6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -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) { From f6e8d7fd73dfd6f7baabc47be21523d12ec2ec5f Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 12 May 2021 10:56:35 +0300 Subject: [PATCH 15/21] Updated on 2026-08-14 --- app/build.gradle | 10 +-- .../tangem/tap/common/extensions/Picasso.kt | 78 +++++++++++++++++++ .../tokens/ui/adapters/CurrenciesAdapter.kt | 26 +++++-- .../wallet/ui/adapters/WalletAdapter.kt | 28 ++++--- dependencies.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 6 files changed, 120 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt diff --git a/app/build.gradle b/app/build.gradle index 44a13aa801..6674f33d44 100644 --- a/app/build.gradle +++ b/app/build.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" diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt new file mode 100644 index 0000000000..1ec834aa43 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt @@ -0,0 +1,78 @@ +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 + + if (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() { + } + }) + } else { + setOfflineCurrencyImage(imageView, textView, token, blockchain) + } +} + +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) +} diff --git a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt index 9fe1ba05bd..0047481627 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/ui/adapters/CurrenciesAdapter.kt @@ -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(DiffUtilCallback) { @@ -104,9 +108,11 @@ class CurrenciesAdapter : ListAdapter 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 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, tokens: List): List { + fun createListOfCurrencies( + blockchains: List, + tokens: List + ): List { val blockchainsTitle = R.string.add_tokens_subtitle_blockchains val tokensTitle = R.string.add_tokens_subtitle_tokens return listOf(TitleListItem(blockchainsTitle)) + diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt index c14d364abf..c82c47f25f 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt @@ -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 -> { diff --git a/dependencies.gradle b/dependencies.gradle index f74991aa26..22dc84c906 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ - kotlin : '1.4.31', - build_gradle: '4.1.3', + kotlin : '1.5.0', + build_gradle: '4.2.0', ] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 385ee4fcc0..c6dab66ff6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -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 From d13df4aedc2bee6a74379da82c2a8c18fcc6d87e Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 12 May 2021 11:53:01 +0300 Subject: [PATCH 16/21] Updated on 2026-08-14 --- app/src/main/assets/erc20_tokens.json | 7 ++++ .../tangem/tap/common/extensions/Picasso.kt | 34 +++++++++++------- app/src/main/res/drawable-ldpi/ic_qcx.png | Bin 0 -> 7787 bytes 3 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 app/src/main/res/drawable-ldpi/ic_qcx.png diff --git a/app/src/main/assets/erc20_tokens.json b/app/src/main/assets/erc20_tokens.json index 9b29c198bb..5f2bfeb629 100644 --- a/app/src/main/assets/erc20_tokens.json +++ b/app/src/main/assets/erc20_tokens.json @@ -629,6 +629,13 @@ "name" : "QASH", "contractAddress" : "0x618e75ac90b12c6049ba3b27f5d5f8651b0037f6" }, + { + "decimalCount" : 8, + "symbol" : "QCX", + "name" : "QuickX Protocol", + "contractAddress" : "0xf9e5af7b42d31d51677c75bbbd37c1986ec79aee", + "customIcon": "qcx" + }, { "decimalCount" : 8, "symbol" : "QRL", diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt index 1ec834aa43..97a952841e 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Picasso.kt @@ -27,23 +27,31 @@ fun Picasso.loadCurrenciesIcon( imageView.colorFilter = null textView.text = null - if (url != null) { - this.load(url) - .placeholder(R.drawable.shape_circle) - ?.into(imageView, - object : Callback { - override fun onError(e: Exception?) { - setOfflineCurrencyImage(imageView, textView, token, blockchain) - } + 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() { - } - }) - } else { - 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, diff --git a/app/src/main/res/drawable-ldpi/ic_qcx.png b/app/src/main/res/drawable-ldpi/ic_qcx.png new file mode 100644 index 0000000000000000000000000000000000000000..68f2ded28763116f2f73132925500ce772a0a649 GIT binary patch literal 7787 zcmd^k=T}o(_cdNcP{AO*2tlL>Dph*#O+dPU5EbcF5CTaEiXb4J(5r${rAm<+=~a4@ zUPAAI03ivy=icZ0`w#p+oRKk-y-)UDd#yR=nmhcNz9#ie)|+HxWYpSPYDQ#aSB(FC zDE|SzxyA2yANaW8sb%3!M)pGE-{%T_f~qnZ83((znzD&s#_n98iHXlV#evhz(QF>9 z-r(5FdD%KxDD*+$2{)K+C95FxO;C|mI~T@+SQLh?@wlD4=>GsIGx=m zUDQjyXz=|*MwWr+KrWHpRl#2I3&vbMpzNVoyb}-leV-frUQ#_ZLz?BiM%%L?aEw+XVczgYQic6;Hkph2FW+5OWZ_a_+bXi5uo{Q? zdpa`+ImEw{)!2;qE$XbMqhqA8CbjJI@=83|*A+*--w4m^cxyITMe$OEFPhzLAT>qA z)oDI-Dx-<@1pPfD_Y2~zid`Ewd^<6f1|#Y zz0b+nbR<_3EQZ#2SaHT@5{uBWiSe*RI(1X*zE(R7HB@9) zIPO|@tIhky%|i9*ESvb?_XEq&zJMjvAl3D@aJqa1#KtFpShz(%T>I|0mcXNXO9eOT z{|PhdStad2pA^C^tf9;-jc{z8JLKorc6R%=iaU#L8s~C5%e{nJSb7=x;6$s4KA$iY zZt3G@)vp=`6iuc%R$9hHEG1*Udwj?}p@X?!qjwPxYSe!5k3Wp!=(e3^AU%{ zd)JA_!I-{f+!^nx;1DcHKk|Ws0>8Iei|vyC=tJ=cx3@%`>0(7?8@gJCV}*PoB@k9c0Np{0B!?_;s^#ndywx zVhQJWmoWz}H+#Ubz7-Cxq%M3btCHXZLZm{g@xkJ;E zS|lU>%~6xhgidGg_|LVZja}ng@gC1A%!LBId{?(Z)L6!5$H*j!MM^H0=QgmS_U0dc z60a3wr9=F!ApDXyTgl2OTIR-0jfg5*gP+J7`4)!POqxUT#<&W z4a4hoXL`*|73z57w*pBDAyX8(B?n-ci5*98TD#bg1x&K(1Wh&P!dE|u>${uJJJXo{ zq?Zx{kkyT81s@(|@S+jwC9S^5;eP&&5#!eYfLI*5hIlORZMDvF$jI@c+g9hH7-qxU zDwbZfSyZ{R8Lf>O$0i{jKaqXzWfUNBD%O}DApih)0{SX=je$zYp?cyhA?G5(CxG9m zhr^sF_c?ooDfi+%JX{ScVY3e?eI~}4pBlyac-#Hx`Gz6+?kbKfdC}QX1~*{J7Ji`$ z$iKecH1?xRsL#piNS;g(*x7;{_oUoe35;~XHq&B z6Pbjtv=@xtTs48?mL|tI`CEy^SiN*#I`M;;WDIZ?1wv;U$^|f_h~ae{a7@Oct(Oyr zW|6yn?Yh{+cbn_>`MHV^$%lOr1v5l|G0Mi9loMJ&g7FJWUdGT(Hu6_XIjL$aRkREN zq&V&|kBjpU8adaIo^c4Crsjw>JQF z{NXIHeJaeJW`&2>=)Ew#_}FTm*rwBi<#LGN1{h2sFwch`XsLiCRhD1Lik z7hJ#1nh=@|r?g}v-^eGZK!rD!K=1!z;PMJvHl#zAgh1ynA)9#A#(Xu7+fE1sO3`bM z--o+4(wTjtj`==QuunQU0@l#tiY-L%Gq{uAvP_B9{`v0o^I&L2(+A;n#MVBcBKIu{ z?CZsAKg2#VqgSFvP1Vo4uzhG4vo4<^^Qk~N%uw>|ZLuPa1S)Xnaj#FBhVpMZ(YDco zIK}T$rO^Ws|4q1@mF~^E=w4C{XAKS^?6axAWgc6R?Y9-fM~kh8r9HXb zmLU7(b?$BS)Kes&9n|!a66n>fnm_vH zJX<)gh-$$E;#W;`cq8n_D4Sv!f|Q{`8Ufo~64!z86`)tq=QjPr8g+QlwqI~0l5ArN z6!>nA5O_J0tQG~O;su}jP&IV;(RK9r_+Eq*97eZbj29-Coa&bLC#*d3q&g#*-imC1 zN&>>TQ{EIZPQ=IHrj+XaHobE?WhdeT0onxiKGq`i~riMH@;ULt(z90

3Cdbh?3NaU%K$Nklijc>9_ax&rVFR`0h?Or6%HjzB1^Nwb^ zSjOr1dHnNWJw*nlj@tp+s9tpwGr?m z?tP03(xggDvRqSAH&_Iq4Wp5KfP-3ht1Jz&c$_P#>W+MyV#I07rBR`6wC=PEZ4b@lU%Jf4$qBQ;z;kX+1`&O?-zvw$Q=FD27?=B>)~G%e7I&lUtc|d2&te zZq`y67NLE5@;$Wd1Y|`j@y`7{px~=A*G`8c42PFzS?U5F0x*>ByF(osarqUd$7j*i_D>p9S6rMX;%i{r>HW&CTg(rZ&d| z`;`h@#^#WVg-zpP?EW5!`y1;tW)1yt@sd*HcCO@(sWacVjiTu<9dF?KP}y?XwT1#B z#ge)*5>JALOJ{(i1K-qKEJW(0v7xned7WRrl*W%iPCO(e=){{%%!Sw-pC5l7OqO)f z#(%b4W}s%T&i(>FdX5T*p|7urq!S@)(R|L8zZ&YLAXQ8Zj~Sn4Y9*kn0j>9Ar>?%5 zxFE0kx0hKQoBux?09Fl5ebA>ehw6_6+uN=tp)Ox; zG5JymlpB1$rhl9N$eYRhg#DdXSWLQcO8y*ZygQ14f#D8x zsHNVed)Gt!E*g7^HYP3v9v>q}s?_vZ4Q{HiTf#CCY(AU1 zCiwiC?T9b~$-I>sN&nC|yRMrsOJQ?p?cq#or*9!?ZnXdI*AAXFL@ZZKv@27pCcn0; zDj`8E=DiRK~dIaZbWRI?MPJiIX6p8Enj@pj0RQE^s2y5Cfc9XbXAbeG^Xb z_nMwgo)yQOpsi0X3Q2N@I-b$BO`(a6MrWa2q%)`{gM5BF6t{Ij@`w`UD-d^THCu>P zEZe~{fDS(c%69pj^*Y?U&V5(c#OWxsZRlOfv-ugmIrIH0Gkyq}l@akDbq5=vxpQ`0 zmyZji@)hZ3h?|MF@pd7N!Yc*DBMZwfo?P|y4L>rNgP(u1)m z)oEX|(9r%*KMNcWf#d15GJ3ZJgh&?p_Iqm&t6j&>zepX>-Vr)6Evf84VAve6TzbcE zg^OwemWnr1ae0F2g?!+m9vrjsweNb*Ws(3L;an#yW~LlT-uAeY%!FlrpNEeaEwNsf zWd86nJo3*t8>J;-;qzxksAaLq<}RuVvXLRY>U0~rv+r4F8rhU#k1lC`rSU2io6YPZ zy8G8uFM%c41sOQ=qXww2@JcR5?;Ia*9===f$&!-dV#R{jqlJ@7wyrw7>o`05V9C?w zUTrq&o>SF`10XOjx7q=Q4l|#61sF%r2Y!xBqi21a90l@i-gb)$!Y`qYWVhX)tmJDv=u`IUVO`BxO_)Q&l7^ z69Go_B}J2^kGLqG|Fc_5Dy6+E*r0;FIxI-kQ11St5BcTD>p#iLuM+S7_@Hi@wg(*S zyA(4^6hrxEnOkw$pBCWdTz`)kifp`z1T*>cjSFl75j0;(R)D1 z;`veQIBiSTCH& z@~o!2c@mNLwQfiM3fsH-ot$aWpl=`)M5+&vC@%<|C#j>@vzb+JLuLWtH5U^`8~E z3U!OrSkl}fsbI&40k#yfjEV?+iAVjsYG7&>Q2#~+F`H#Qeg(-@8;cNM8VKa{O5!U* z{)rJ!P5YPCggNnBD6^jhmFiS%w96}uk5iVQ?O%nW($4+1mXu-QnKl{v{2k zQ*h=yfmj4JV5ClX%=Rq(K@qX-EAaCDMg>{B7Yxcgc(;#= zMP!!GT>#ci8;Bc_U!d90$r$%OaD`52FlJk4>(b?nbibz!}e=D z?Do#`9x>6o)jqSQCoHi<&wBynRatU`Jr)OM{cG_OlwX=#PrtVGC2QO~g7A@VCs% zE>?>70BwR!p`h1eKf*hFfxLXo1^jW~x4={DCkDe~Z zsjrItBb_n-5%ZDPgr#j8Y72f37->B#$I^{UnpkDJLMQyqu7eAb;r>xt^JaA}u4TIUgQWEeCmHQ`E(&Yw@ZS7lr=_4T{KXeJ^kdOFu z6Ig>d;AIo<|J;;d7rCj@F_KSI`0(*IPA>a@d`@dO@ueD6#Ge7tXCefj{Q|JI==IKb ze+8hB!m*3D*SA#=?j{H?fKPHy~@)E z?%ZIdS7WOA!tF^?~lFuuL~M95sVWp!|koVB`at`8mym`^@nmC$uNm17r76*L2+B`cSEpIQnPz>C* zJa}>E%j)1$b8tMUMf)z!5o!=~Oe9*6$B0PRi7Q?5h*<|{DO>}{sdb8miT}4NSc0QF z6m#`#9d~AC>dr+${F^c!Dvcn!P*;x-XUm!c+AK5)(eR&Ddq9A2QI_J#J|4gj)!WeH zrXz>N${5Vm?qx^sq_XKL*|>Wnl3QvnU?s-B=iY5`CF)}N%;YUEg^By)WnAapZQ@`~ zDFETB>m@m!veONNmel^elAQQIcLg~s!c28Zapm4Y6Lx(y9=JUw(^l74D^+ Date: Wed, 12 May 2021 16:27:54 +0300 Subject: [PATCH 17/21] Updated on 2026-08-14 --- .../warningMessage/WarningMessage.kt | 22 +-- .../warningMessage/WarningMessagesManager.kt | 15 +++ .../tap/features/wallet/redux/WalletAction.kt | 1 + .../tap/features/wallet/redux/WalletState.kt | 3 +- .../redux/middlewares/WarningsMiddleware.kt | 11 +- .../wallet/redux/reducers/WalletReducer.kt | 3 + .../ui/adapters/WarningMessagesAdapter.kt | 125 +++++++++++------- .../ui/dialogs/SignedHashesWarningDialog.kt | 33 +++++ .../wallet/ui/wallet/MultiWalletView.kt | 20 ++- .../wallet/ui/wallet/SingleWalletView.kt | 6 + .../main/res/values/strings_untranslated.xml | 9 +- 11 files changed, 178 insertions(+), 70 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SignedHashesWarningDialog.kt 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 ab94c0d563..8494a61786 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 @@ -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, - private val blockchains: List?, - 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, + private val blockchains: List?, + @StringRes val titleResId: Int? = null, + @StringRes val messageResId: Int? = null, + val origin: Origin = Origin.Remote, + @StringRes val buttonTextId: Int? = null, ) { val blockchainList: List? by lazy { blockchains?.map { Blockchain.fromId(it.toUpperCase()) } @@ -43,6 +46,7 @@ data class WarningMessage( 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 81739ee7b5..8bde4ae283 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 @@ -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( "", "", 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 5b3877338d..18ffaa31b1 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 @@ -111,6 +111,7 @@ sealed class WalletAction : Action { object ShowDialog : WalletAction() { object QrCode : WalletAction() object ScanFails : WalletAction() + object SignedHashesMultiWalletDialog : WalletAction() } object HideDialog : WalletAction() 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 244f7f026d..6d7bc13229 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 @@ -112,7 +112,8 @@ sealed class WalletDialog: StateDialog { ) : WalletDialog() data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog() - object ScanFailsDialog: WalletDialog() + object ScanFailsDialog : WalletDialog() + object SignedHashesMultiWalletDialog : WalletDialog() } enum class ProgressState { Loading, Done, Error } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index 79ab8e3feb..46a7693492 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -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 { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 957e9a32d3..06f40d4d68 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -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) } 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 a15a4e3e9a..80b739b21c 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 @@ -37,9 +37,11 @@ class WarningMessagesAdapter : ListAdapter(Dif } object DiffUtilCallback : DiffUtil.ItemCallback() { - 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 diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SignedHashesWarningDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SignedHashesWarningDialog.kt new file mode 100644 index 0000000000..9aaf4b88d6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SignedHashesWarningDialog.kt @@ -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() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index 6e69a80b7d..5deff50687 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index 97f2618542..56eba3612c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -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 diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 238bc7a845..762001572e 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -103,5 +103,12 @@ this wallet. This card might be a production sample or counterfeit - + Important security information \u26A0 + This card has signed transactions in the past + Learn more + 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. + I understand From 812831937c9a96087fadcbaf1d7f31f0b133776e Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 12 May 2021 21:40:31 +0300 Subject: [PATCH 18/21] Updated on 2026-08-14 --- app/build.gradle | 10 +-- .../tap/common/redux/global/GlobalAction.kt | 7 +- .../tap/common/redux/global/GlobalReducer.kt | 23 ++++++- .../com/tangem/tap/domain/TangemSigner.kt | 65 +++++++++++++++++++ .../features/details/redux/DetailsReducer.kt | 5 +- .../features/details/redux/DetailsState.kt | 1 + .../features/details/ui/DetailsFragment.kt | 4 ++ .../send/redux/middlewares/SendMiddleware.kt | 15 ++++- app/src/main/res/layout/fragment_details.xml | 26 +++++++- 9 files changed, 141 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/TangemSigner.kt diff --git a/app/build.gradle b/app/build.gradle index 44a13aa801..6674f33d44 100644 --- a/app/build.gradle +++ b/app/build.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" 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 2a2a612757..38f45e4ca7 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 @@ -24,7 +24,12 @@ sealed class GlobalAction : Action { data class Success(val appCurrency: FiatCurrencyName) : GlobalAction() } - data class UpdateWalletSignedHashes(val walletSignedHashes: Int?) : GlobalAction() + data class UpdateWalletSignedHashes( + val walletSignedHashes: Int?, + val remainingSignatures: Int?, + val walletPublicKey: ByteArray, + ) : GlobalAction() + data class HideWarningMessage(val warning: WarningMessage) : GlobalAction() data class UpdateSecurityOptions(val securityOption: SecurityOption) : GlobalAction() 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 7b7e74d399..c7ed09486a 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 @@ -1,5 +1,6 @@ package com.tangem.tap.common.redux.global +import com.tangem.commands.wallet.WalletIndex import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.details.redux.SecurityOption import org.rekotlin.Action @@ -32,13 +33,13 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.UpdateSecurityOptions -> { val card = when (action.securityOption) { SecurityOption.LongTap -> globalState.scanNoteResponse?.card?.copy( - isPin1Default = true, isPin2Default = true + isPin1Default = true, isPin2Default = true ) SecurityOption.PassCode -> globalState.scanNoteResponse?.card?.copy( - isPin1Default = true, isPin2Default = false + isPin1Default = true, isPin2Default = false ) SecurityOption.AccessCode -> globalState.scanNoteResponse?.card?.copy( - isPin1Default = false, isPin2Default = true + isPin1Default = false, isPin2Default = true ) } if (card != null) { @@ -47,6 +48,22 @@ fun globalReducer(action: Action, state: AppState): GlobalState { globalState } } + is GlobalAction.UpdateWalletSignedHashes -> { + val wallet = globalState.scanNoteResponse?.card + ?.wallet(WalletIndex.PublicKey(action.walletPublicKey)) + ?.copy( + signedHashes = action.walletSignedHashes, + remainingSignatures = action.remainingSignatures + ) + val card = globalState.scanNoteResponse?.card + wallet?.let { globalState.scanNoteResponse.card.updateWallet(wallet) } + + if (card != null) { + globalState.copy(scanNoteResponse = globalState.scanNoteResponse.copy(card = card)) + } else { + globalState + } + } is GlobalAction.SetFeedbackManager -> { globalState.copy(feedbackManager = action.feedbackManager) } diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt new file mode 100644 index 0000000000..7a3556b33a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt @@ -0,0 +1,65 @@ +package com.tangem.tap.domain + +import com.tangem.Message +import com.tangem.TangemSdk +import com.tangem.blockchain.common.TransactionSigner +import com.tangem.commands.SignCommand +import com.tangem.commands.SignResponse +import com.tangem.commands.wallet.WalletIndex +import com.tangem.common.CompletionResult +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +class TangemSigner( + private val tangemSdk: TangemSdk, + private val initialMessage: Message, + private val signerCallback: (SignResponse) -> Unit +) : TransactionSigner { + + override suspend fun sign( + hash: ByteArray, + cardId: String, + walletPublicKey: ByteArray + ): CompletionResult = + suspendCoroutine { continuation -> + val command = SignCommand(arrayOf(hash), WalletIndex.PublicKey(walletPublicKey)) + tangemSdk.startSessionWithRunnable( + runnable = command, + cardId = cardId, + initialMessage = initialMessage, + ) { result -> + when (result) { + is CompletionResult.Success -> { + signerCallback(result.data) + continuation.resume(CompletionResult.Success(result.data.signatures.first())) + } + is CompletionResult.Failure -> + continuation.resume(CompletionResult.Failure(result.error)) + } + } + } + + + override suspend fun sign( + hashes: List, + cardId: String, + walletPublicKey: ByteArray + ): CompletionResult> = + suspendCoroutine { continuation -> + val command = SignCommand(hashes.toTypedArray(), WalletIndex.PublicKey(walletPublicKey)) + tangemSdk.startSessionWithRunnable( + runnable = command, + cardId = cardId, + initialMessage = initialMessage, + ) { result -> + when (result) { + is CompletionResult.Success -> { + signerCallback(result.data) + continuation.resume(CompletionResult.Success(result.data.signatures)) + } + is CompletionResult.Failure -> + continuation.resume(CompletionResult.Failure(result.error)) + } + } + } +} \ No newline at end of file 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 4ec1681b66..a699f2ee45 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 @@ -5,6 +5,7 @@ import com.tangem.commands.common.card.Card import com.tangem.commands.common.card.masks.Settings import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapWorkarounds +import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.domain.twins.isTwinCard @@ -203,5 +204,5 @@ private fun prepareAllowedSecurityOptions(card: Card): EnumSet { private fun Card.toCardInfo(): CardInfo? { val cardId = this.cardId.chunked(4).joinToString(separator = " ") val issuer = this.cardData?.issuerName ?: return null - return CardInfo(cardId, issuer) -} \ No newline at end of file + val signedHashes = this.signedHashesCount() + return CardInfo(cardId, issuer, signedHashes)} \ No newline at end of file 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 22fca3db45..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 @@ -26,6 +26,7 @@ data class DetailsState( data class CardInfo( val cardId: String, val issuer: String, + val signedHashes: Int, ) enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty } 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 fc793be4cc..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 @@ -74,8 +74,12 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber + store.dispatch( + GlobalAction.UpdateWalletSignedHashes( + walletSignedHashes = signResponse.walletSignedHashes, + remainingSignatures = signResponse.walletRemainingSignatures, + walletPublicKey = walletManager.wallet.publicKey + ) + ) + } val result = (walletManager as TransactionSender).send(txData, signer) withContext(Dispatchers.Main) { when (result) { diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 69b8b5cf55..fc33c31d7d 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -90,6 +90,30 @@ app:layout_constraintTop_toBottomOf="@id/tv_card_id" tools:text="Tangem" /> + + + + + app:layout_constraintTop_toBottomOf="@id/tv_signed_hashes_title" /> Date: Wed, 12 May 2021 22:43:31 +0300 Subject: [PATCH 19/21] Updated on 2026-08-14 --- .../common/redux/global/GlobalMidlleware.kt | 3 ++ .../warningMessage/WarningMessage.kt | 1 + .../warningMessage/WarningMessagesManager.kt | 16 +++++++ .../com/tangem/tap/domain/extensions/Card.kt | 5 +- .../tap/features/wallet/redux/WalletAction.kt | 1 + .../redux/middlewares/WarningsMiddleware.kt | 46 +++++++++++++++++-- .../ui/adapters/WarningMessagesAdapter.kt | 8 ++-- .../main/res/values/strings_untranslated.xml | 2 + 8 files changed, 73 insertions(+), 9 deletions(-) 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 dce356f1be..b06dc1b5df 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 @@ -53,6 +53,9 @@ val globalMiddleware: Middleware = { dispatch, appState -> is GlobalAction.SendFeedback -> { store.state.globalState.feedbackManager?.send(action.emailData) } + is GlobalAction.UpdateWalletSignedHashes -> { + store.dispatch(WalletAction.Warnings.CheckRemainingSignatures(action.remainingSignatures)) + } } nextDispatch(action) } 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 8494a61786..3c3ad5f5ce 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 @@ -19,6 +19,7 @@ data class WarningMessage( @StringRes val messageResId: Int? = null, val origin: Origin = Origin.Remote, @StringRes val buttonTextId: Int? = null, + val messageFormatArg: String? = null ) { val blockchainList: List? by lazy { blockchains?.map { Blockchain.fromId(it.toUpperCase()) } 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 8bde4ae283..59e91a7f6a 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 @@ -1,5 +1,6 @@ package com.tangem.tap.domain.configurable.warningMessage +import com.tangem.TangemSdkError import com.tangem.blockchain.common.Blockchain import com.tangem.tangem_sdk_new.ui.animation.VoidCallback import com.tangem.tap.common.extensions.containsAny @@ -140,5 +141,20 @@ class WarningMessagesManager( R.string.warning_failed_to_verify_card_message, WarningMessage.Origin.Local ) + + fun remainingSignaturesNotEnough(remainingSignatures: Int): WarningMessage = WarningMessage( + title = "", + message = "", + type = WarningMessage.Type.Permanent, + priority = WarningMessage.Priority.Critical, + listOf(WarningMessage.Location.MainScreen), + blockchains = null, + titleResId = R.string.alert_title, + messageResId = R.string.warning_low_signatures_format, + origin = WarningMessage.Origin.Local, + messageFormatArg = remainingSignatures.toString() + ) + + const val REMAINING_SIGNATURES_WARNING = 10 } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index 4dd07f00f5..eb60189b4f 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -41,4 +41,7 @@ fun Card.hasSignedHashes(): Boolean { fun Card.signedHashesCount(): Int { return getWallets().map { it.signedHashes ?: 0 }.sum() -} \ No newline at end of file +} + +val Card.remainingSignatures: Int? + get() = this.getSingleWallet()?.remainingSignatures \ No newline at end of file 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 18ffaa31b1..405e96be66 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 @@ -68,6 +68,7 @@ sealed class WalletAction : Action { object SetNeverToShow : Warnings() object RemindLater : Warnings() } + class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings() } data class UpdateWallet(val currency: CryptoCurrencyName? = null) : WalletAction() { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index 46a7693492..211e5ef2b1 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -16,6 +16,7 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.extensions.hasSignedHashes +import com.tangem.tap.domain.extensions.remainingSignatures import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.twins.isTwinCard import com.tangem.tap.features.wallet.redux.WalletAction @@ -48,6 +49,17 @@ class WarningsMiddleware { is WalletAction.Warnings.AppRating.SetNeverToShow -> { preferencesStorage.appRatingLaunchObserver.setNeverToShow() } + is WalletAction.Warnings.CheckRemainingSignatures -> { + if (action.remainingSignatures != null && + action.remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING + ) { + addWarningMessage( + warning = + WarningMessagesManager.remainingSignaturesNotEnough(action.remainingSignatures), + autoUpdate = true + ) + } + } } } @@ -67,6 +79,7 @@ class WarningsMiddleware { private fun showCardWarningsIfNeeded(globalState: GlobalState?) { globalState?.scanNoteResponse?.card?.let { card -> globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local) + showWarningLowRemainingSignaturesIfNeeded(card) if (card.getType() != CardType.Release) { addWarningMessage(WarningMessagesManager.devCardWarning()) } else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) { @@ -74,7 +87,8 @@ class WarningsMiddleware { } if (card.getType() == CardType.Release) { if (globalState.scanNoteResponse.verifyResponse?.verificationState == - VerifyCardState.VerifiedOffline) { + VerifyCardState.VerifiedOffline + ) { addWarningMessage(WarningMessagesManager.onlineVerificationFailed()) } } @@ -82,8 +96,21 @@ class WarningsMiddleware { } } + private fun showWarningLowRemainingSignaturesIfNeeded(card: Card) { + val remainingSignatures = card.remainingSignatures + if (remainingSignatures != null && + remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING + ) { + addWarningMessage( + WarningMessagesManager.remainingSignaturesNotEnough( + remainingSignatures + ) + ) + } + } + private fun checkIfWarningNeeded( - card: Card + card: Card ): WarningMessage? { if (card.isTwinCard()) return null @@ -133,9 +160,15 @@ class WarningsMiddleware { } is SimpleResult.Failure -> if (result.error is BlockchainSdkError.SignatureCountNotMatched) { - addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true) + addWarningMessage( + WarningMessagesManager.alreadySignedHashesWarning(), + true + ) } else if (signedHashes > 0) { - addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true) + addWarningMessage( + WarningMessagesManager.alreadySignedHashesWarning(), + true + ) } } } @@ -153,6 +186,9 @@ class WarningsMiddleware { private fun getWarnings(): List { val warningManager = store.state.globalState.warningManager ?: return emptyList() - return warningManager.getWarnings(WarningMessage.Location.MainScreen, store.state.walletState.blockchains) + return warningManager.getWarnings( + WarningMessage.Location.MainScreen, + store.state.walletState.blockchains + ) } } \ 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 80b739b21c..c262bbb35c 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 @@ -54,11 +54,13 @@ 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, formatArgs: String? = null) = + if (resId == null) default else view.context.getString(resId, formatArgs) view.tv_title.text = getString(warning.titleResId, warning.title) - view.tv_message.text = getString(warning.messageResId, warning.message) + view.tv_message.text = getString( + resId = warning.messageResId, default = warning.message, formatArgs = warning.messageFormatArg + ) } private fun setBgColor(priority: WarningMessage.Priority) { diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 762001572e..339ccae0b2 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -111,4 +111,6 @@ this wallet. \n\nIt’s perfectly safe in all other respects. \n\nTangem is the only hardware wallet to offer signature count protection. I understand + + There are only %s signatures available on this card. You must withdraw all of your funds. From 8dd8537bef3598ddec8e3c0211cdcdf43719b131 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 14 May 2021 15:52:17 +0300 Subject: [PATCH 20/21] Updated on 2026-08-14 --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6674f33d44..823fe723d5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,9 +75,9 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-5' - implementation 'com.tangem:core:develop-10' - implementation 'com.tangem:sdk:develop-10' + implementation 'com.tangem:blockchain:develop-8' + implementation 'com.tangem:core:develop-14' + implementation 'com.tangem:sdk:develop-14' // WebView implementation "androidx.browser:browser:1.3.0" From 0252d788b7f873d403e9ff3e4af5b6e9d62b391a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 17 May 2021 11:43:54 +0000 Subject: [PATCH 21/21] Updated on 2026-08-14 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 823fe723d5..7248d6b0bc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,8 +76,8 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation 'com.tangem:blockchain:develop-8' - implementation 'com.tangem:core:develop-14' - implementation 'com.tangem:sdk:develop-14' + implementation 'com.tangem:core:develop-15' + implementation 'com.tangem:sdk:develop-15' // WebView implementation "androidx.browser:browser:1.3.0"