diff --git a/app/build.gradle b/app/build.gradle index bdfb6fba80..bc26208e29 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" 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 b262647600..de18ac1a98 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 @@ -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() } \ No newline at end of file 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 b3f496719c..66fbb80140 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 @@ -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 } \ No newline at end of file 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 c4dc1b2f7b..c8ec821b08 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -34,11 +34,19 @@ class TangemSdkManager(val activity: ComponentActivity) { } suspend fun setPasscode(cardId: String?): CompletionResult { - 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 { - 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 { 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 7f5e2810db..cd19178850 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 @@ -62,7 +62,8 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { - 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) { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsSecurityFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsSecurityFragment.kt index 60050df949..f5a81039f2 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsSecurityFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsSecurityFragment.kt @@ -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 { @@ -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) { + 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 } } \ No newline at end of file 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 1a82a4d1db..320eac8f91 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 @@ -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], "") } 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 2142515916..4c58fa1f51 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 @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt index 8f279e2540..8644404390 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletMiddleware.kt @@ -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) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt index c751d15e4f..ab941817ae 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt @@ -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, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt index d466f26036..4a8cb2ca4f 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt @@ -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()) diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 7582d69d9f..7dac8c7e31 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -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" diff --git a/app/src/main/res/layout/fragment_details_security.xml b/app/src/main/res/layout/fragment_details_security.xml index 11d1ea1b4c..17aa16be23 100644 --- a/app/src/main/res/layout/fragment_details_security.xml +++ b/app/src/main/res/layout/fragment_details_security.xml @@ -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" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aaf5fac8fb..23ca3ebfbf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,6 +27,7 @@ Create PayID Verified Balance Blockchain is unreachable + Transaction in progress 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. Uh oh! Balance is loading… @@ -106,6 +107,7 @@ Manage security Erase wallet Save changes + Continue Long Tap Passcode Access Code