Updated on 2026-08-14

This commit is contained in:
Tangem 2020-09-28 17:36:43 +00:00
commit 9e83415c6e
15 changed files with 135 additions and 42 deletions

View file

@ -64,9 +64,9 @@ dependencies {
implementation 'com.google.android.material:material:1.2.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
implementation 'com.tangem:blockchain:1.49.0'
implementation 'com.tangem:core:1.57.0'
implementation 'com.tangem:sdk:1.57.0'
implementation 'com.tangem:blockchain:1.56.0'
implementation 'com.tangem:core:1.61.0'
implementation 'com.tangem:sdk:1.61.0'
//lifecycle
implementation "androidx.lifecycle:lifecycle-runtime:2.2.0"

View file

@ -14,4 +14,5 @@ sealed class GlobalAction : Action {
object RestoreAppCurrency : GlobalAction() {
data class Success(val appCurrency: FiatCurrencyName) : GlobalAction()
}
data class UpdateWalletSignedHashes(val walletSignedHashes: Int) : GlobalAction()
}

View file

@ -7,22 +7,32 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
if (action !is GlobalAction) return state.globalState
var newState = state.globalState
val globalState = state.globalState
when (action) {
return when (action) {
is GlobalAction.SaveScanNoteResponse ->
newState = newState.copy(scanNoteResponse = action.scanNoteResponse)
globalState.copy(scanNoteResponse = action.scanNoteResponse)
is GlobalAction.SetFiatRate -> {
val rates = newState.conversionRates.rates.toMutableMap()
val rates = globalState.conversionRates.rates.toMutableMap()
rates[action.fiatRates.first] = action.fiatRates.second
newState = newState.copy(conversionRates = ConversionRates(rates))
globalState.copy(conversionRates = ConversionRates(rates))
}
is GlobalAction.ChangeAppCurrency -> {
newState = newState.copy(appCurrency = action.appCurrency, conversionRates = ConversionRates(mapOf()))
globalState.copy(appCurrency = action.appCurrency, conversionRates = ConversionRates(mapOf()))
}
is GlobalAction.RestoreAppCurrency.Success -> {
newState = newState.copy(appCurrency = action.appCurrency, conversionRates = ConversionRates(mapOf()))
globalState.copy(appCurrency = action.appCurrency, conversionRates = ConversionRates(mapOf()))
}
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
}
}
else -> globalState
}
return newState
}

View file

@ -34,11 +34,19 @@ class TangemSdkManager(val activity: ComponentActivity) {
}
suspend fun setPasscode(cardId: String?): CompletionResult<SetPinResponse> {
return runTaskAsyncReturnOnMain(SetPinCommand.setPin2(null), cardId)
return runTaskAsyncReturnOnMain(SetPinCommand(
pinType = PinType.Pin2,
newPin1 = tangemSdk.config.defaultPin1.calculateSha256(),
newPin2 = null
), cardId)
}
suspend fun setAccessCode(cardId: String?): CompletionResult<SetPinResponse> {
return runTaskAsyncReturnOnMain(SetPinCommand.setPin1(null), cardId)
return runTaskAsyncReturnOnMain(SetPinCommand(
pinType = PinType.Pin1,
newPin1 = null,
newPin2 = tangemSdk.config.defaultPin2.calculateSha256()
), cardId)
}
suspend fun setLongTap(cardId: String?): CompletionResult<SetPinResponse> {

View file

@ -62,7 +62,8 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable<ScanNoteRespons
}
private fun getErrorIfExcludedCard(card: Card): TangemError? {
if (card.cardData?.productMask?.contains(Product.Note) != true) {
if (card.cardData?.productMask != null &&
card.cardData?.productMask?.contains(Product.Note) != true) {
return TapSdkError.CardForDifferentApp
}
if (excludedBatches.contains(card.cardData?.batchId)) {

View file

@ -111,11 +111,16 @@ private fun handleSecurityAction(
): DetailsState {
return when (action) {
is DetailsAction.ManageSecurity.OpenSecurity -> {
val prohibitDefaultPin = state.card?.settingsMask?.contains(Settings.ProhibitDefaultPIN1) == true
val allowSetPin1 = state.card?.settingsMask?.contains(Settings.AllowSetPIN1) != false
val allowSetPin2 = state.card?.settingsMask?.contains(Settings.AllowSetPIN2) != false
val isDefaultPin1 = state.card?.isPin1Default != false
val isDefaultPin2 = state.card?.isPin2Default != false
if (state.card?.isPin2Default == null) {
return state.copy(securityScreenState = state.securityScreenState?.copy(
allowedOptions = EnumSet.noneOf(SecurityOption::class.java)
))
}
val prohibitDefaultPin = state.card.settingsMask?.contains(Settings.ProhibitDefaultPIN1) == true
val allowSetPin1 = state.card.settingsMask?.contains(Settings.AllowSetPIN1) != false
val allowSetPin2 = state.card.settingsMask?.contains(Settings.AllowSetPIN2) != false
val isDefaultPin1 = state.card.isPin1Default != false
val isDefaultPin2 = state.card.isPin2Default != false
val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java)
if ((isDefaultPin1 && isDefaultPin2) || !prohibitDefaultPin) {

View file

@ -14,6 +14,7 @@ import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_details_confirm.toolbar
import kotlinx.android.synthetic.main.fragment_details_security.*
import org.rekotlin.StoreSubscriber
import java.util.*
class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
StoreSubscriber<DetailsState> {
@ -73,11 +74,53 @@ class DetailsSecurityFragment : Fragment(R.layout.fragment_details_security),
override fun newState(state: DetailsState) {
if (activity == null) return
selectSecurityOption(state.securityScreenState?.selectedOption)
for (option in SecurityOption.values()) {
enableOptions(
option,
state.securityScreenState?.allowedOptions
?: EnumSet.noneOf(SecurityOption::class.java)
)
}
}
private fun selectSecurityOption(securityOption: SecurityOption?) {
radiobutton_long_tap.isChecked = securityOption == SecurityOption.LongTap
radiobutton_passcode.isChecked = securityOption == SecurityOption.PassCode
radiobutton_access_code.isChecked = securityOption == SecurityOption.AccessCode
radiobutton_long_tap.isChecked =
securityOption == SecurityOption.LongTap && radiobutton_long_tap.isEnabled
radiobutton_passcode.isChecked =
securityOption == SecurityOption.PassCode && radiobutton_passcode.isEnabled
radiobutton_access_code.isChecked =
securityOption == SecurityOption.AccessCode && radiobutton_access_code.isEnabled
}
private fun enableOptions(option: SecurityOption, allowedOptions: EnumSet<SecurityOption>) {
when (option) {
SecurityOption.LongTap -> enableLongTap(allowedOptions.contains(option))
SecurityOption.PassCode -> enablePasscode(allowedOptions.contains(option))
SecurityOption.AccessCode -> enableAccessCode(allowedOptions.contains(option))
}
}
private fun enableLongTap(enable: Boolean) {
val alpha = if (enable) 1f else 0.5f
tv_long_tap_description.alpha = alpha
tv_long_tap_title.alpha = alpha
radiobutton_long_tap.alpha = alpha
radiobutton_long_tap.isEnabled = enable
}
private fun enablePasscode(enable: Boolean) {
val alpha = if (enable) 1f else 0.5f
tv_passcode_description.alpha = alpha
tv_passcode_title.alpha = alpha
radiobutton_passcode.alpha = alpha
radiobutton_passcode.isEnabled = enable
}
private fun enableAccessCode(enable: Boolean) {
val alpha = if (enable) 1f else 0.5f
tv_access_code_description.alpha = alpha
tv_access_code_title.alpha = alpha
radiobutton_access_code.alpha = alpha
radiobutton_access_code.isEnabled = enable
}
}

View file

@ -128,7 +128,7 @@ internal class AddressPayIdMiddleware {
//TODO: move to the blockchainSDK
private fun extractAddressFromShareUri(shareUri: String): String {
val sharePrefix = listOf("bitcoin:", "ethereum:", "ripple:", "litecoin:")
val sharePrefix = listOf("bitcoin:", "ethereum:", "xrpl:", "litecoin:", "bnb:")
val prefixes = sharePrefix.filter { shareUri.contains(it) }
return if (prefixes.isEmpty()) shareUri else shareUri.replace(prefixes[0], "")
}

View file

@ -1,10 +1,11 @@
package com.tangem.tap.features.send.redux.middlewares
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.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.features.send.redux.AddressPayIdActionUi
@ -62,12 +63,13 @@ private fun verifyAndSendTransaction(appState: AppState?, dispatch: (Action) ->
val result = (walletManager as TransactionSender).send(txData, Signer(tangemSdk))
withContext(Dispatchers.Main) {
when (result) {
is SimpleResult.Success -> {
is Result.Success -> {
dispatch(SendAction.SendSuccess)
dispatch(GlobalAction.UpdateWalletSignedHashes(result.data.walletSignedHashes))
dispatch(WalletAction.UpdateWallet)
dispatch(NavigationAction.PopBackTo())
}
is SimpleResult.Failure -> {
is Result.Failure -> {
when (result.error) {
is CreateAccountUnderfunded -> {
val error = result.error as CreateAccountUnderfunded

View file

@ -11,6 +11,8 @@ import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.commands.Card
import com.tangem.commands.common.network.Result
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.CardType
import com.tangem.common.extensions.getType
import com.tangem.common.extensions.toHexString
import com.tangem.tap.common.extensions.copyToClipboard
import com.tangem.tap.common.redux.AppState
@ -195,9 +197,9 @@ private fun checkIfWarningNeeded(
card: Card, signatureCountValidator: SignatureCountValidator? = null
): WarningType? {
// if (card.getType() != CardType.Release) {
// return WarningType.DevCard
// }
if (card.getType() != CardType.Release) {
return WarningType.DevCard
}
return if (signatureCountValidator == null) {
if (card.walletSignedHashes ?: 0 > 0) {

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.wallet.redux
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.Wallet
import com.tangem.commands.common.network.TangemService
import com.tangem.common.extensions.isZero
import com.tangem.common.extensions.toHexString
import com.tangem.tap.common.extensions.toFiatString
@ -140,13 +141,15 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
))
}
is WalletAction.LoadArtwork -> {
// TODO: form URL in Card SDK
var artworkAddress = "https://verify.tangem.com/card/artwork"
artworkAddress += "?artworkId=${action.artworkId}"
artworkAddress += "&CID=${store.state.globalState.scanNoteResponse?.card?.cardId}"
artworkAddress += "&publicKey=${store.state.globalState.scanNoteResponse?.card?.cardPublicKey?.toHexString()}"
val artwork = if (action.artworkId != null) {
Artwork(artworkId = artworkAddress)
val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
val cardPublicKey = store.state.globalState.scanNoteResponse?.card?.cardPublicKey?.toHexString()
val artworkUrl = if (cardId != null && cardPublicKey != null && action.artworkId != null) {
TangemService.getUrlForArtwork(cardId, cardPublicKey, action.artworkId)
} else {
null
}
val artwork = if (artworkUrl != null) {
Artwork(artworkId = artworkUrl)
} else {
Artwork(artworkResId = R.drawable.card_default)
}
@ -221,10 +224,15 @@ private fun onWalletLoaded(wallet: Wallet, walletState: WalletState): WalletStat
.toPendingTransactions(wallet.address)
val sendButtonEnabled = amount?.isZero() == false && pendingTransactions.isEmpty()
val balanceStatus = if (pendingTransactions.isNotEmpty()) {
BalanceStatus.TransactionInProgress
} else {
BalanceStatus.VerifiedOnline
}
return walletState.copy(
state = ProgressState.Done, wallet = wallet,
currencyData = BalanceWidgetData(
BalanceStatus.VerifiedOnline, wallet.blockchain.fullName,
balanceStatus, wallet.blockchain.fullName,
currencySymbol = wallet.blockchain.currency,
formattedAmount,
token = tokenData,

View file

@ -12,6 +12,7 @@ import kotlinx.android.synthetic.main.layout_token.view.*
enum class BalanceStatus {
VerifiedOnline,
TransactionInProgress,
Unreachable,
Loading,
NoAccount,
@ -56,14 +57,22 @@ class BalanceWidget(
showStatus(R.id.tv_status_loading)
}
BalanceStatus.VerifiedOnline -> {
BalanceStatus.VerifiedOnline, BalanceStatus.TransactionInProgress -> {
fragment.l_balance.show()
fragment.l_balance_error.hide()
fragment.tv_currency.text = data.currency
fragment.tv_amount.text = data.amount
fragment.tv_fiat_amount.show()
fragment.tv_fiat_amount.text = data.fiatAmount
showStatus(R.id.tv_status_verified)
val statusView = if (data.status == BalanceStatus.VerifiedOnline) {
R.id.tv_status_verified
} else {
fragment.tv_status_error.text =
fragment.getText(R.string.wallet_transaction_in_progress)
R.id.group_error
}
showStatus(statusView)
fragment.tv_status_error_message.hide()
if (data.token != null) {
fragment.l_token.show()
@ -83,6 +92,8 @@ class BalanceWidget(
fragment.tv_currency.text = data.currency
fragment.tv_amount.text = ""
fragment.tv_status_error_message.text = data.errorMessage
fragment.tv_status_error.text =
fragment.getString(R.string.wallet_blockchain_is_unreachable)
showStatus(R.id.group_error)
fragment.tv_status_error_message.show(!data.errorMessage.isNullOrBlank())

View file

@ -195,7 +195,7 @@
android:id="@+id/tv_security_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="7dp"
android:paddingTop="10dp"
android:paddingBottom="7dp"
android:text="@string/details_manage_security"
android:textColor="@color/darkGray6"
@ -208,7 +208,7 @@
android:id="@+id/tv_erase_wallet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingTop="7dp"
android:paddingBottom="7dp"
android:drawablePadding="15dp"
android:textColor="@color/darkGray6"

View file

@ -185,7 +185,7 @@
android:layout_marginStart="7dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="30dp"
android:text="@string/details_save_changes"
android:text="@string/details_continue"
app:icon="@drawable/ic_save"
app:layout_constraintStart_toEndOf="@+id/guideline"
app:layout_constraintEnd_toEndOf="parent"

View file

@ -27,6 +27,7 @@
<string name="wallet_create_payid">Create PayID</string>
<string name="wallet_verified_balance">Verified Balance</string>
<string name="wallet_blockchain_is_unreachable">Blockchain is unreachable</string>
<string name="wallet_transaction_in_progress">Transaction in progress</string>
<string name="wallet_unknown_blockchain">Your Tangem card was made to work with a different application. Please see the name and instructions on your card, and install the correct app.</string>
<string name="wallet_unknown_blockchain_title">Uh oh!</string>
<string name="wallet_balance_is_loading">Balance is loading…</string>
@ -106,6 +107,7 @@
<string name="details_manage_security">Manage security</string>
<string name="details_erase_wallet">Erase wallet</string>
<string name="details_save_changes">Save changes</string>
<string name="details_continue">Continue</string>
<string name="details_manage_security_long_tap">Long Tap</string>
<string name="details_manage_security_passcode">Passcode</string>
<string name="details_manage_security_access_code">Access Code</string>