Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-18 18:05:18 +08:00
parent e98780ff93
commit 479d142807
283 changed files with 2338 additions and 2730 deletions

View file

@ -20,8 +20,8 @@ import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.NotificationsHandler
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.common.shop.GooglePayService
import com.tangem.tap.common.shop.GooglePayService.Companion.LOAD_PAYMENT_DATA_REQUEST_CODE
import com.tangem.tap.common.shop.googlepay.GooglePayService
import com.tangem.tap.common.shop.googlepay.GooglePayService.Companion.LOAD_PAYMENT_DATA_REQUEST_CODE
import com.tangem.tap.common.shop.googlepay.GooglePayUtil.createPaymentsClient
import com.tangem.tap.domain.TangemSdkManager
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
@ -120,7 +120,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
val isScannedBefore = store.state.globalState.scanResponse != null
val isOnboardingServiceActive = store.state.globalState.onboardingState.onboardingStarted
val shopOpened = store.state.shopState.total != null
if (backStackIsEmpty || (!isOnboardingServiceActive && !isScannedBefore && !shopOpened)) {
@Suppress("ComplexCondition")
if (backStackIsEmpty || !isOnboardingServiceActive && !isScannedBefore && !shopOpened) {
if (userWalletsListManager.hasSavedUserWallets) {
store.dispatchOnMain(WelcomeAction.HandleDeepLink(intent))
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Welcome))

View file

@ -173,7 +173,7 @@ class TapApplication : Application(), ImageLoaderFactory {
private fun initWithConfigDependency(config: Config) {
shopService = TangemShopService(this, config.shopify!!)
initAnalytics(this, config)
initFeedbackManager(this, preferencesStorage, config)
initFeedbackManager(this, preferencesStorage)
}
private fun initAnalytics(application: Application, config: Config) {
@ -196,7 +196,7 @@ class TapApplication : Application(), ImageLoaderFactory {
factory.build(Analytics, buildData)
}
private fun initFeedbackManager(context: Context, preferencesStorage: PreferencesStorage, config: Config) {
private fun initFeedbackManager(context: Context, preferencesStorage: PreferencesStorage) {
fun initAdditionalFeedbackInfo(context: Context): AdditionalFeedbackInfo = AdditionalFeedbackInfo().apply {
appVersion = try {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)

View file

@ -4,4 +4,4 @@ package com.tangem.tap
[REDACTED_AUTHOR]
*/
val DELAY_SDK_DIALOG_CLOSE = 1400L
const val DELAY_SDK_DIALOG_CLOSE = 1400L

View file

@ -56,6 +56,7 @@ class DialogManager : StoreSubscriber<GlobalState> {
store.unsubscribe(this)
}
@Suppress("LongMethod", "ComplexMethod")
override fun newState(state: GlobalState) {
if (state.dialog == null) {
dialog?.dismiss()
@ -73,7 +74,7 @@ class DialogManager : StoreSubscriber<GlobalState> {
is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context)
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context)
is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context)
is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(context)
is OnboardingDialog.InterruptOnboarding -> InterruptOnboardingDialog.create(context, state.dialog)
is WalletConnectDialog.UnsupportedCard ->
SimpleAlertDialog.create(

View file

@ -44,7 +44,7 @@ class KeyboardObserver(activity: Activity) {
onKeyboardListener?.invoke(isShow)
}
private fun isSoftKeyChanged() = ((lastWindowHeight - getWindowHeight()).absoluteValue) == getSoftKeyButtonHeight()
private fun isSoftKeyChanged() = (lastWindowHeight - getWindowHeight()).absoluteValue == getSoftKeyButtonHeight()
private fun getSoftKeyButtonHeight(): Int {
val applicationDisplayHeight = DisplayMetrics().apply {
@ -57,5 +57,4 @@ class KeyboardObserver(activity: Activity) {
return realDisplayHeight - applicationDisplayHeight
}
}

View file

@ -40,9 +40,10 @@ typealias TestAction = Pair<String, () -> Unit>
class TestActionsBottomSheetDialog(
private val appDialog: AppDialog.TestActionsDialog,
context: Context
context: Context,
) : BottomSheetDialog(context) {
@Suppress("MagicNumber")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.analytics
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.analytics.api.ErrorEventLogger
import com.tangem.analytics.api.EventLogger
import com.tangem.core.analytics.api.ErrorEventLogger
import com.tangem.core.analytics.api.EventLogger
import timber.log.Timber
class AnalyticsEventsLogger(

View file

@ -2,10 +2,11 @@ package com.tangem.tap.common.analytics
import com.tangem.common.core.TangemSdkError
object TangemSdk {
object TangemSdkErrorMapper {
// This mapping is performed to group errors in FirebaseCrashlytics.
// At the moment, the errors in Crashlytics can only be grouped by their place of creation (class and line).
@Suppress("LongMethod", "ComplexMethod")
fun map(error: TangemSdkError): TangemSdkError {
return when (error) {
is TangemSdkError.TagLost -> TangemSdkError.TagLost()
@ -90,7 +91,8 @@ object TangemSdk {
is TangemSdkError.BackupFailedEmptyWallets -> TangemSdkError.BackupFailedEmptyWallets()
is TangemSdkError.BackupFailedNotEmptyWallets -> TangemSdkError.BackupFailedNotEmptyWallets()
is TangemSdkError.NoActiveBackup -> TangemSdkError.NoActiveBackup()
is TangemSdkError.ResetBackupFailedHasBackupedWallets -> TangemSdkError.ResetBackupFailedHasBackupedWallets()
is TangemSdkError.ResetBackupFailedHasBackupedWallets ->
TangemSdkError.ResetBackupFailedHasBackupedWallets()
is TangemSdkError.BackupServiceInvalidState -> TangemSdkError.BackupServiceInvalidState()
is TangemSdkError.NoBackupCardForIndex -> TangemSdkError.NoBackupCardForIndex()
is TangemSdkError.EmptyBackupCards -> TangemSdkError.EmptyBackupCards()

View file

@ -53,7 +53,9 @@ class BasicTopUpEventConverter(
}
}
private fun AnalyticsParam.CardBalanceState.Companion.from(walletsData: List<WalletData>): AnalyticsParam.CardBalanceState {
private fun AnalyticsParam.CardBalanceState.Companion.from(
walletsData: List<WalletData>,
): AnalyticsParam.CardBalanceState {
val totalCryptoAmount = walletsData.calculateTotalCryptoAmount()
return when {
totalCryptoAmount.isZero() -> AnalyticsParam.CardBalanceState.Empty

View file

@ -8,7 +8,10 @@ sealed class AnalyticsParam {
class Currency(currency: com.tangem.tap.features.wallet.models.Currency) : CurrencyType(currency.currencySymbol)
class Blockchain(blockchain: com.tangem.blockchain.common.Blockchain) : CurrencyType(blockchain.currency)
class Token(token: com.tangem.blockchain.common.Token) : CurrencyType(token.symbol)
class FiatCurrency(fiatCurrency: com.tangem.tap.common.entities.FiatCurrency) : CurrencyType(fiatCurrency.symbol)
class FiatCurrency(
fiatCurrency: com.tangem.tap.common.entities.FiatCurrency,
) : CurrencyType(fiatCurrency.symbol)
class Amount(amount: com.tangem.blockchain.common.Amount) : CurrencyType(amount.currencySymbol)
}

View file

@ -4,7 +4,7 @@ import android.app.Application
import com.amplitude.api.Amplitude
import com.amplitude.api.AmplitudeClient
import com.tangem.common.Converter
import com.tangem.analytics.api.EventLogger
import com.tangem.core.analytics.api.EventLogger
import org.json.JSONObject
/**

View file

@ -2,7 +2,7 @@ package com.tangem.tap.common.analytics.handlers.appsFlyer
import android.content.Context
import com.appsflyer.AppsFlyerLib
import com.tangem.analytics.api.EventLogger
import com.tangem.core.analytics.api.EventLogger
/**
[REDACTED_AUTHOR]

View file

@ -6,8 +6,8 @@ import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import com.tangem.analytics.api.ErrorEventLogger
import com.tangem.analytics.api.EventLogger
import com.tangem.core.analytics.api.ErrorEventLogger
import com.tangem.core.analytics.api.EventLogger
/**
[REDACTED_AUTHOR]

View file

@ -11,6 +11,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
@Suppress("MagicNumber")
@Composable
fun TextAutoSize(
modifier: Modifier = Modifier,
@ -46,7 +47,7 @@ fun TextAutoSize(
} else {
readyToDraw.value = true
}
}
},
)
}

View file

@ -17,6 +17,7 @@ import com.tangem.tap.common.extensions.ValueCallback
/**
[REDACTED_AUTHOR]
*/
@Suppress("MagicNumber")
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun <T> OutlinedSpinner(
@ -95,7 +96,7 @@ class ClosePopupTrigger {
@Preview
@Composable
fun TestSpinnerPreview() {
Scaffold() {
Scaffold {
OutlinedSpinner(
label = "Blockchain name",
itemList = listOf(Blockchain.values()),

View file

@ -78,6 +78,7 @@ fun OutlinedTextFieldWidget(
}
}
@Suppress("LongMethod", "NestedBlockDepth", "MagicNumber", "MaxLineLength")
@Composable
private fun OutlinedProgressTextField(
modifier: Modifier = Modifier,
@ -127,7 +128,7 @@ private fun OutlinedProgressTextField(
logger.log("$isNotUserInput: внешние данные ОДИНАКОВЫ с данными в поле")
} else {
logger.log("$isNotUserInput: внешние данные РАЗЛИЧАЮТСЯ с данными в поле")
if ((textDebouncer.emittedValue != textDebouncer.debounced) || textDebouncer.emitsCountBeforeDebounce > 0) {
if (textDebouncer.emittedValue != textDebouncer.debounced || textDebouncer.emitsCountBeforeDebounce > 0) {
logger.log("$isNotUserInput: пользователь ВВОДИТ данные -> внешние данные игнорируем, ждем RECOMPOSE")
} else {
logger.log("$isNotUserInput: пользователь НЕ вводит данные -> пытаемся обработать внешние данные")

View file

@ -125,10 +125,6 @@ private fun PinElement(
}
}
@Composable
private fun HiddenInputField() {
}
private fun createPinSymbolsList(size: Int, text: String): List<String?> = List(size) {
try {
text[it].toString()

View file

@ -1,20 +0,0 @@
package com.tangem.tap.common.compose.extensions
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
/**
[REDACTED_AUTHOR]
*/
fun Color.toAndroidGraphicsColor(): Int {
val argb = this.toArgb()
return android.graphics.Color.argb(argb.alpha, argb.red, argb.green, argb.blue)
}
fun Color.parse(hexColor: String): Color {
return Color(hexColor.removePrefix("#").toInt(16))
}

View file

@ -13,7 +13,8 @@ import androidx.core.graphics.drawable.toBitmap
*/
@Composable
fun asImageBitmap(@DrawableRes drawableId: Int): ImageBitmap {
val drawable = AppCompatResources.getDrawable(LocalContext.current, drawableId)
?: throw NullPointerException()
val drawable = requireNotNull(AppCompatResources.getDrawable(LocalContext.current, drawableId)) {
"drawable is null"
}
return drawable.toBitmap().asImageBitmap()
}

View file

@ -6,8 +6,7 @@ import com.tangem.operations.backup.BackupService
/**
[REDACTED_AUTHOR]
*/
@Suppress("MagicNumber")
fun BackupService.primaryCardIsSaltPayVisa(): Boolean {
return primaryCardId?.slice(0..3)?.let {
SaltPayWorkaround.isVisaBatchId(it)
} ?: false
return primaryCardId?.slice(0..3)?.let(SaltPayWorkaround::isVisaBatchId) ?: false
}

View file

@ -4,12 +4,14 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.io.ByteArrayOutputStream
@Suppress("MagicNumber")
fun Bitmap.toByteArray(): ByteArray {
val stream = ByteArrayOutputStream()
this.compress(Bitmap.CompressFormat.JPEG, 20, stream)
return stream.toByteArray()
}
@Suppress("MagicNumber")
fun ByteArray.toBitmap(): Bitmap {
return BitmapFactory.decodeByteArray(this, 0, this.size)
}

View file

@ -5,16 +5,17 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.remove
import com.tangem.wallet.R
@Suppress("ComplexMethod")
@DrawableRes
fun Blockchain.getGreyedOutIconRes(): Int {
return when (this) {
Blockchain.Arbitrum, Blockchain.ArbitrumTestnet -> R.drawable.ic_arbitrum_no_color
// Blockchain.Ducatus -> R.drawable.ic_ducatus
Blockchain.Bitcoin, Blockchain.BitcoinTestnet,-> R.drawable.ic_bitcoin_no_color
Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> R.drawable.ic_bitcoin_no_color
Blockchain.BitcoinCash -> R.drawable.ic_bitcoin_cash_no_color
Blockchain.Litecoin -> R.drawable.ic_litecoin_no_color
Blockchain.Ethereum, Blockchain.EthereumTestnet, -> R.drawable.ic_eth_no_color
Blockchain.EthereumClassic, Blockchain.EthereumClassicTestnet, -> R.drawable.ic_eth_no_color
Blockchain.Ethereum, Blockchain.EthereumTestnet -> R.drawable.ic_eth_no_color
Blockchain.EthereumClassic, Blockchain.EthereumClassicTestnet -> R.drawable.ic_eth_no_color
Blockchain.RSK -> R.drawable.ic_rsk_no_color
Blockchain.Cardano, Blockchain.CardanoShelley -> R.drawable.ic_cardano_no_color
Blockchain.Tezos -> R.drawable.ic_tezos_no_color
@ -24,7 +25,8 @@ fun Blockchain.getGreyedOutIconRes(): Int {
Blockchain.Polygon, Blockchain.PolygonTestnet -> R.drawable.ic_polygon_no_color
Blockchain.Solana, Blockchain.SolanaTestnet -> R.drawable.ic_solana_no_color
Blockchain.Fantom, Blockchain.FantomTestnet -> R.drawable.ic_fantom_no_color
Blockchain.BSC, Blockchain.BSCTestnet, Blockchain.Binance, Blockchain.BinanceTestnet -> R.drawable.ic_bsc_no_color
Blockchain.BSC, Blockchain.BSCTestnet, Blockchain.Binance, Blockchain.BinanceTestnet ->
R.drawable.ic_bsc_no_color
Blockchain.Dogecoin -> R.drawable.ic_dogecoin_no_color
Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_no_color
Blockchain.Gnosis -> R.drawable.ic_gnosis_no_color
@ -49,4 +51,4 @@ fun Blockchain.getNetworkName(): String {
}
val Blockchain.fullNameWithoutTestnet
get() = this.fullName.remove(" Testnet")
get() = this.fullName.remove(" Testnet")

View file

@ -1,12 +1,10 @@
package com.tangem.tap.common.extensions
import android.os.Bundle
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import com.tangem.common.extensions.VoidCallback
import com.tangem.feature.referral.ReferralFragment
import com.tangem.feature.swap.presentation.SwapFragment
import com.tangem.tap.common.redux.navigation.AppScreen
@ -84,21 +82,15 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? {
} else {
0
}
val tag = if (indexOfLastFragment < this.supportFragmentManager.backStackEntryCount)
val tag = if (indexOfLastFragment < this.supportFragmentManager.backStackEntryCount) {
this.supportFragmentManager.getBackStackEntryAt(indexOfLastFragment).name
else null
} else {
null
}
return tag?.let { AppScreen.valueOf(tag) }
}
fun FragmentActivity.addOnBackPressedDispatcher(
isEnabled: Boolean = true,
onBackPressed: VoidCallback,
): OnBackPressedCallback = (object : OnBackPressedCallback(isEnabled) {
override fun handleOnBackPressed() {
onBackPressed()
}
}).also { this.onBackPressedDispatcher.addCallback(it) }
@Suppress("ComplexMethod")
private fun fragmentFactory(screen: AppScreen): Fragment {
return when (screen) {
AppScreen.Home -> HomeFragment()

View file

@ -13,7 +13,7 @@ import java.util.*
//todo move extensions to utils
fun BigDecimal.toFormattedString(
decimals: Int, roundingMode: RoundingMode = RoundingMode.DOWN, locale: Locale = Locale.US
decimals: Int, roundingMode: RoundingMode = RoundingMode.DOWN, locale: Locale = Locale.US,
): String {
val symbols = DecimalFormatSymbols(locale)
val df = DecimalFormat()
@ -25,9 +25,10 @@ fun BigDecimal.toFormattedString(
return df.format(this)
}
@Suppress("MagicNumber")
fun BigDecimal.toFormattedCurrencyString(
decimals: Int, currency: String, roundingMode: RoundingMode = RoundingMode.DOWN,
limitNumberOfDecimals: Boolean = true
limitNumberOfDecimals: Boolean = true,
): String {
val decimalsForRounding = if (limitNumberOfDecimals) {
if (decimals > 8) 8 else decimals
@ -35,13 +36,13 @@ fun BigDecimal.toFormattedCurrencyString(
decimals
}
val formattedAmount = this.toFormattedString(
decimals = decimalsForRounding, roundingMode = roundingMode
decimals = decimalsForRounding, roundingMode = roundingMode,
)
return "$formattedAmount $currency"
}
fun BigDecimal.toFiatRateString(
fiatCurrencyName: String
fiatCurrencyName: String,
): String {
val value = this
.setScale(2, RoundingMode.HALF_UP)
@ -52,7 +53,7 @@ fun BigDecimal.toFiatRateString(
fun BigDecimal.toFiatString(
rateValue: BigDecimal,
fiatCurrencyName: String,
formatWithSpaces: Boolean = false
formatWithSpaces: Boolean = false,
): String {
val fiatValue = rateValue.multiply(this)
return fiatValue.toFormattedFiatValue(fiatCurrencyName, formatWithSpaces)
@ -65,7 +66,7 @@ fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal {
fun BigDecimal.toFormattedFiatValue(
fiatCurrencyName: String,
formatWithSpaces: Boolean = false
formatWithSpaces: Boolean = false,
): String {
val fiatValue = this.setScale(2, RoundingMode.HALF_UP)
.let { if (formatWithSpaces) it.formatWithSpaces() else it }
@ -82,9 +83,7 @@ fun BigDecimal.scaleToFiat(applyPrecision: Boolean = false): BigDecimal {
if (this.isZero()) return this
val scaledFiat = this.setScale(2, RoundingMode.DOWN)
return if (scaledFiat.isZero() && applyPrecision) this.setPrecision(1)
else scaledFiat
return if (scaledFiat.isZero() && applyPrecision) this.setPrecision(1) else scaledFiat
}
fun BigDecimal.setPrecision(precision: Int, roundingMode: RoundingMode = RoundingMode.DOWN): BigDecimal {
@ -109,7 +108,7 @@ fun BigDecimal.isLessThanOrEqual(value: BigDecimal): Boolean {
fun BigDecimal.formatAmountAsSpannedString(
currencySymbol: String,
reminderPartSizeProportion: Float = 0.7f
reminderPartSizeProportion: Float = 0.7f,
): SpannedString {
val amount = this
.setScale(2, RoundingMode.HALF_UP)
@ -125,11 +124,12 @@ fun BigDecimal.formatAmountAsSpannedString(
append(
"$reminder $currencySymbol",
RelativeSizeSpan(reminderPartSizeProportion),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
}
@Suppress("MagicNumber")
fun BigDecimal.formatWithSpaces(): String {
val str = this.toString()
var integerStr = str.substringBefore('.')

View file

@ -29,7 +29,7 @@ fun String.colorSegment(
context: Context,
color: Int,
startIndex: Int = 0,
endIndex: Int = this.length
endIndex: Int = this.length,
): Spannable {
return this.toSpannable()
.also { spannable ->
@ -37,11 +37,12 @@ fun String.colorSegment(
ForegroundColorSpan(ContextCompat.getColor(context, color)),
startIndex,
endIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}
}
@Suppress("MagicNumber")
fun String.toQrCode(): Bitmap {
val hintMap = Hashtable<EncodeHintType, Any>()
hintMap[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.M // H = 30% damage

View file

@ -23,8 +23,7 @@ fun EditText.update(text: String?) {
this.setText(text)
if (!isFocused || textLength == 0) return
if (cursorPosition == 0) setSelection(textLength)
else setSelection(cursorPosition)
if (cursorPosition == 0) setSelection(textLength) else setSelection(cursorPosition)
}
fun EditText.setOnImeActionListener(action: Int, handler: (EditText) -> Unit) {
@ -56,13 +55,4 @@ fun TextInputLayout.enableError(enable: Boolean, errorMessage: String? = null) {
error = null
isErrorEnabled = false
}
}
fun TextView.isEllipsized(): Boolean {
val layout = this.layout
if (layout != null) {
val lines: Int = layout.lineCount
return (lines > 0) && (layout.getEllipsisCount(lines - 1) > 0)
}
return false
}

View file

@ -6,19 +6,23 @@ import androidx.core.graphics.luminance
import androidx.core.graphics.toColorInt
import com.tangem.blockchain.common.Token
@Suppress("MagicNumber")
@ColorInt
fun Token.getColor(isTestnet: Boolean = false): Int {
val defaultColor = "#C7C7CC".toColorInt() // equivalent to R.color.lightGray4
return if (isTestnet)
defaultColor
else try {
("#" + this.contractAddress.subSequence(2..7).toString()).toColorInt()
} catch (exception: Exception) {
return if (isTestnet) {
defaultColor
} else {
try {
("#" + this.contractAddress.subSequence(2..7).toString()).toColorInt()
} catch (exception: Exception) {
defaultColor
}
}
}
@Suppress("MagicNumber")
@ColorInt
fun Token.getTextColor(isTestnet: Boolean = false): Int = when {
isTestnet -> Color.WHITE

View file

@ -1,16 +1,14 @@
@file:Suppress("TooManyFunctions")
package com.tangem.tap.common.extensions
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.View
@ -25,11 +23,6 @@ import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.google.android.material.card.MaterialCardView
import com.tangem.common.extensions.VoidCallback
fun Fragment.getDrawable(@DrawableRes drawableResId: Int): Drawable? {
return ContextCompat.getDrawable(requireContext(), drawableResId)
}
fun Context.getDrawableCompat(@DrawableRes drawableResId: Int): Drawable? {
return ContextCompat.getDrawable(this, drawableResId)
@ -62,17 +55,8 @@ fun View.getQuantityString(@PluralsRes id: Int, quantity: Int): String {
return context.resources.getQuantityString(id, quantity, quantity)
}
fun View.getResourceName(): String {
return try {
resources.getResourceEntryName(id)
} catch (ex: Resources.NotFoundException) {
"Not found"
}
}
fun View.show(show: Boolean, invokeBeforeStateChanged: (() -> Unit)? = null) {
return if (show) this.show(invokeBeforeStateChanged)
else this.hide(invokeBeforeStateChanged)
return if (show) this.show(invokeBeforeStateChanged) else this.hide(invokeBeforeStateChanged)
}
fun View.show(invokeBeforeStateChanged: (() -> Unit)? = null) {
@ -111,12 +95,6 @@ fun Context.pixelsToDp(pixels: Int): Int {
.toInt()
}
fun Context.dpToPixels(dp: Float): Float =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, this.resources.displayMetrics)
fun Context.pixelsToDp(pixels: Float): Float =
(pixels / (resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT))
tailrec fun Context?.getActivity(): Activity? = this as? Activity
?: (this as? ContextWrapper)?.baseContext?.getActivity()
@ -136,19 +114,6 @@ fun MaterialCardView.setMargins(
this.layoutParams = params
}
fun Activity.setSystemBarTextColor(setTextDark: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val flags = this.window.decorView.systemUiVisibility
// Update the SystemUiVisibility dependening on whether we want a Light or Dark theme.
this.window.decorView.systemUiVisibility =
if (setTextDark) {
flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
} else {
flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
}
}
fun View.hideKeyboard() {
val inputMethodManager =
context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? InputMethodManager
@ -185,21 +150,6 @@ fun Fragment.shareText(text: String) {
requireContext().shareText(text)
}
fun Context.safeStartActivity(
intent: Intent,
options: Bundle? = null,
fallback: ((ActivityNotFoundException) -> Unit)? = null,
finally: VoidCallback? = null,
) {
try {
this.startActivity(intent, options)
} catch (ex: ActivityNotFoundException) {
fallback?.invoke(ex)
} finally {
finally?.invoke()
}
}
fun View.getString(resId: Int, vararg formatArgs: Any?): String {
return context.getString(resId, *formatArgs)
}

View file

@ -21,6 +21,7 @@ import timber.log.Timber
/**
[REDACTED_AUTHOR]
*/
@Suppress("MagicNumber")
suspend fun WalletManager.safeUpdate(): Result<Wallet> = try {
val scanResponse = store.state.globalState.scanResponse
@ -72,8 +73,7 @@ fun WalletManager?.getAddressData(): AddressData? {
val wallet = this?.wallet ?: return null
val addressDataList = wallet.createAddressesData()
return if (addressDataList.isEmpty()) null
else addressDataList[0]
return if (addressDataList.isEmpty()) null else addressDataList[0]
}
fun <T> WalletManager.Companion.stub(): T {

View file

@ -48,6 +48,9 @@ class AdditionalFeedbackInfo {
var fee: String = ""
var token: String = ""
private val Address.name: String
get() = type.javaClass.simpleName
fun setCardInfo(data: ScanResponse) {
cardId = data.card.cardId
cardBlockchain = data.walletData?.blockchain ?: ""
@ -108,6 +111,7 @@ class AdditionalFeedbackInfo {
}
}
@Suppress("MagicNumber")
private fun Wallet.formatAddressWith(with: String, mapAddress: (Address) -> String): String {
return if (addresses.size == 1) {
getExploreUrl(address)
@ -117,7 +121,4 @@ class AdditionalFeedbackInfo {
.joinToString("\n")
}
}
private val Address.name: String
get() = type.javaClass.simpleName
}

View file

@ -15,6 +15,7 @@ interface FeedbackData {
fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String
@Suppress("MagicNumber")
fun joinTogether(context: Context, infoHolder: AdditionalFeedbackInfo): String {
return StringBuilder().apply {
append(context.getString(mainMessageResId))
@ -42,6 +43,7 @@ class ScanFailsEmail : FeedbackData {
override val subjectResId: Int = R.string.feedback_subject_scan_failed
override val mainMessageResId: Int = R.string.feedback_preface_scan_failed
@Suppress("MagicNumber")
override fun joinTogether(context: Context, infoHolder: AdditionalFeedbackInfo): String = StringBuilder().apply {
append(context.getString(mainMessageResId))
breakLine(4)

View file

@ -17,13 +17,13 @@ class TestLeapfrogFragment : Fragment(R.layout.test_leapfrog_fragment) {
private lateinit var twinsCardWidget: TwinsCardWidget
private val binding: TestLeapfrogFragmentBinding by viewBinding(TestLeapfrogFragmentBinding::bind)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val inflater = TransitionInflater.from(requireContext())
exitTransition = inflater.inflateTransition(R.transition.fade)
}
@Suppress("MagicNumber")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

View file

@ -16,11 +16,6 @@ import me.dm7.barcodescanner.zxing.ZXingScannerView
[REDACTED_AUTHOR]
*/
class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
companion object {
val SCAN_QR_REQUEST_CODE = 1001
val SCAN_RESULT = "scanResult"
}
private lateinit var mScannerView: ZXingScannerView
override fun onCreate(state: Bundle?) {
@ -29,7 +24,6 @@ class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
setContentView(mScannerView)
if (!permissionIsGranted()) requestPermission()
}
override fun onResume() {
@ -66,4 +60,9 @@ class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
private fun requestPermission() {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CODE)
}
companion object {
const val SCAN_QR_REQUEST_CODE = 1001
const val SCAN_RESULT = "scanResult"
}
}

View file

@ -88,6 +88,7 @@ val notificationsMiddleware: Middleware<AppState> = { dispatch, state ->
}
}
@Suppress("ComplexMethod")
private fun handleNotificationAction(action: Action) {
if (action is Debug && !BuildConfig.DEBUG) return

View file

@ -30,10 +30,8 @@ import org.rekotlin.DispatchFunction
import org.rekotlin.Middleware
import java.util.*
class GlobalMiddleware {
companion object {
val handler = globalMiddlewareHandler
}
object GlobalMiddleware {
val handler = globalMiddlewareHandler
}
private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState ->
@ -45,6 +43,7 @@ private val globalMiddlewareHandler: Middleware<AppState> = { dispatch, appState
}
}
@Suppress("LongMethod", "ComplexMethod")
private fun handleAction(action: Action, appState: () -> AppState?, dispatch: DispatchFunction) {
when (action) {
is GlobalAction.ScanFailsCounter.ChooseBehavior -> {

View file

@ -9,6 +9,7 @@ import com.tangem.tap.preferencesStorage
import com.tangem.tap.proxy.AppStateHolder
import org.rekotlin.Action
@Suppress("LongMethod", "ComplexMethod")
fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolder): GlobalState {
if (action !is GlobalAction) return state.globalState

View file

@ -10,7 +10,7 @@ import java.lang.ref.WeakReference
data class FragmentShareTransition(
val shareElements: List<ShareElement>,
val enterTransitionSet: TransitionSet,
val exitTransitionSet: TransitionSet
val exitTransitionSet: TransitionSet,
)
/**
@ -24,12 +24,12 @@ class ShareElement(view: View, name: String? = null) {
init {
name?.let { view.transitionName = it }
elementName = view.transitionName
?: throw UnsupportedOperationException("ShareElement require the name")
?: throw UnsupportedOperationException("ShareElement require the name")
wView = WeakReference(view)
}
companion object {
val imvFrontCard = "imv_front_card"
val imvBackCard = "imv_back_card"
const val imvFrontCard = "imv_front_card"
const val imvBackCard = "imv_back_card"
}
}

View file

@ -4,10 +4,8 @@ import com.tangem.tap.common.extensions.getPreviousScreen
import com.tangem.tap.common.redux.AppState
import org.rekotlin.Action
class NavigationReducer {
companion object {
fun reduce(action: Action, state: AppState): NavigationState = internalReduce(action, state)
}
object NavigationReducer {
fun reduce(action: Action, state: AppState): NavigationState = internalReduce(action, state)
}
private fun internalReduce(action: Action, state: AppState): NavigationState {

View file

@ -10,6 +10,7 @@ import com.tangem.tap.common.extensions.filterNotNull
import com.tangem.tap.common.shop.data.ProductType
import com.tangem.tap.common.shop.data.TangemProduct
import com.tangem.tap.common.shop.data.TotalSum
import com.tangem.tap.common.shop.googlepay.GooglePayService
import com.tangem.tap.common.shop.shopify.ShopifyService
import com.tangem.tap.common.shop.shopify.ShopifyShop
import com.tangem.tap.common.shop.shopify.data.CheckoutItem

View file

@ -1,4 +1,4 @@
package com.tangem.tap.common.shop
package com.tangem.tap.common.shop.googlepay
import android.app.Activity
import android.app.Activity.RESULT_CANCELED
@ -6,9 +6,12 @@ import android.app.Activity.RESULT_OK
import android.content.Intent
import android.util.Log
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.wallet.*
import com.google.android.gms.wallet.AutoResolveHelper
import com.google.android.gms.wallet.IsReadyToPayRequest
import com.google.android.gms.wallet.PaymentData
import com.google.android.gms.wallet.PaymentDataRequest
import com.google.android.gms.wallet.PaymentsClient
import com.tangem.common.core.TangemSdkError
import com.tangem.tap.common.shop.googlepay.GooglePayUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.json.JSONException
@ -48,7 +51,7 @@ class GooglePayService(private val paymentsClient: PaymentsClient, private val a
totalPriceCents,
currencyCode = currencyCode,
countryCode = "RU",
merchantID = merchantID
merchantID = merchantID,
)
if (paymentDataRequestJson == null) {
Timber.e("RequestPayment: can't fetch payment data request")
@ -57,7 +60,7 @@ class GooglePayService(private val paymentsClient: PaymentsClient, private val a
val request = PaymentDataRequest.fromJson(paymentDataRequestJson.toString())
AutoResolveHelper.resolveTask(
paymentsClient.loadPaymentData(request), activity, LOAD_PAYMENT_DATA_REQUEST_CODE
paymentsClient.loadPaymentData(request), activity, LOAD_PAYMENT_DATA_REQUEST_CODE,
)
}
@ -81,7 +84,6 @@ class GooglePayService(private val paymentsClient: PaymentsClient, private val a
} else {
Result.failure(Exception("$statusCode"))
}
}
else -> Result.failure(Exception("Unknown Status"))
}
@ -117,7 +119,6 @@ class GooglePayService(private val paymentsClient: PaymentsClient, private val a
.getString("token")
return GooglePayResponse(address, token)
} catch (e: JSONException) {
Log.e("handlePaymentSuccess", "Error: " + e.toString())
}
@ -144,5 +145,5 @@ data class Address(
val address3: String,
val locality: String,
val administrativeArea: String,
val sortingCode: String
val sortingCode: String,
)

View file

@ -14,20 +14,7 @@ object GooglePayUtil {
put("apiVersionMinor", 0)
}
private fun gatewayTokenizationSpecification(merchantID: String): JSONObject {
return JSONObject().apply {
put("type", "PAYMENT_GATEWAY")
put(
"parameters", JSONObject(
mapOf(
"gateway" to "shopify",
"gatewayMerchantId" to merchantID
)
)
)
}
}
@Suppress("MagicNumber")
private val allowedCardNetworks = JSONArray(
listOf(
"AMEX",
@ -35,17 +22,34 @@ object GooglePayUtil {
"INTERAC",
"JCB",
"MASTERCARD",
"VISA"
)
"VISA",
),
)
private val allowedCardAuthMethods = JSONArray(
listOf(
"PAN_ONLY",
"CRYPTOGRAM_3DS"
)
"CRYPTOGRAM_3DS",
),
)
private val merchantInfo: JSONObject = JSONObject().put("merchantName", "Example Merchant")
private fun gatewayTokenizationSpecification(merchantID: String): JSONObject {
return JSONObject().apply {
put("type", "PAYMENT_GATEWAY")
put(
"parameters",
JSONObject(
mapOf(
"gateway" to "shopify",
"gatewayMerchantId" to merchantID,
),
),
)
}
}
private fun baseCardPaymentMethod(): JSONObject {
return JSONObject().apply {
@ -53,9 +57,12 @@ object GooglePayUtil {
put("allowedAuthMethods", allowedCardAuthMethods)
put("allowedCardNetworks", allowedCardNetworks)
put("billingAddressRequired", true)
put("billingAddressParameters", JSONObject().apply {
put("format", "FULL")
})
put(
"billingAddressParameters",
JSONObject().apply {
put("format", "FULL")
},
)
}
put("type", "CARD")
@ -83,7 +90,6 @@ object GooglePayUtil {
baseRequest.apply {
put("allowedPaymentMethods", JSONArray().put(baseCardPaymentMethod()))
}
} catch (e: JSONException) {
null
}
@ -92,7 +98,7 @@ object GooglePayUtil {
private fun getTransactionInfo(
price: String,
countryCode: String,
currencyCode: String
currencyCode: String,
): JSONObject {
return JSONObject().apply {
put("totalPrice", price)
@ -102,15 +108,11 @@ object GooglePayUtil {
}
}
private val merchantInfo: JSONObject =
JSONObject().put("merchantName", "Example Merchant")
fun getPaymentDataRequest(
price: String,
countryCode: String,
currencyCode: String,
merchantID: String
merchantID: String,
): JSONObject? {
try {
return baseRequest.apply {
@ -133,5 +135,4 @@ object GooglePayUtil {
}
}
const val PAYMENTS_ENVIRONMENT = WalletConstants.ENVIRONMENT_TEST

View file

@ -16,11 +16,10 @@ import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@Suppress("LargeClass")
class ShopifyService(private val application: Application, val shop: ShopifyShop) {
val client: GraphClient by lazy { initClient() }
suspend fun getShopName(): Result<String> {
val query = query { rootQuery: QueryRootQuery ->
rootQuery
@ -40,16 +39,15 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
}
}
@Suppress("MagicNumber")
suspend fun getProducts(collectionTitleFilter: String? = null): Result<List<Product>> {
val filter = collectionTitleFilter?.let { "title:\"$it\"" }
val query = query { rootQuery: QueryRootQuery ->
rootQuery
.collections(
{ arg -> arg.first(250).query(filter) },
) { collectionConnectionQuery ->
collectionConnectionQuery.collectionFieldsFragment()
}
rootQuery.collections(
{ arg -> arg.first(250).query(filter) },
CollectionConnectionQuery::collectionFieldsFragment,
)
}
return when (val result = queryAsync(query)) {
is GraphCallResult.Success -> {
@ -78,7 +76,7 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
}
}
val retryHandler = RetryHandler.build<QueryRoot>(
1, TimeUnit.SECONDS
1, TimeUnit.SECONDS,
) {
this.retryWhen { result ->
when (result) {
@ -100,7 +98,6 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
} else {
Result.failure(ShopifyError.Unknown)
}
}
is GraphCallResult.Failure -> {
Result.failure(result.error)
@ -110,10 +107,9 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
suspend fun createCheckout(
checkoutItems: List<CheckoutItem>,
checkoutID: ID? = null
checkoutID: ID? = null,
): Result<Checkout> {
val storefrontLineItems: MutableList<CheckoutLineItemInput> = checkoutItems
.map { CheckoutLineItemInput(it.quantity, it.id) }.toMutableList()
@ -121,13 +117,13 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
mutation { mutationQuery: MutationQuery ->
mutationQuery
.checkoutLineItemsReplace(
storefrontLineItems, checkoutID
storefrontLineItems, checkoutID,
) { payloadQuery: CheckoutLineItemsReplacePayloadQuery ->
payloadQuery
.checkout { checkoutQuery: CheckoutQuery ->
checkoutQuery.checkoutFieldsFragment()
}
.userErrors() { userErrorQuery: CheckoutUserErrorQuery ->
.userErrors { userErrorQuery: CheckoutUserErrorQuery ->
userErrorQuery
.field()
.message()
@ -137,18 +133,18 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
} else {
val input = CheckoutCreateInput()
.setLineItemsInput(
Input.value(storefrontLineItems)
Input.value(storefrontLineItems),
)
mutation { mutationQuery: MutationQuery ->
mutationQuery
.checkoutCreate(
input
input,
) { payloadQuery: CheckoutCreatePayloadQuery ->
payloadQuery
.checkout { checkoutQuery: CheckoutQuery ->
checkoutQuery.checkoutFieldsFragment()
}
.checkoutUserErrors() { userErrorQuery: CheckoutUserErrorQuery ->
.checkoutUserErrors { userErrorQuery: CheckoutUserErrorQuery ->
userErrorQuery
.field()
.message()
@ -163,13 +159,13 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
val query = mutation { mutationQuery: MutationQuery ->
mutationQuery
.checkoutDiscountCodeApplyV2(
discountCode, checkoutID
discountCode, checkoutID,
) { payloadQuery: CheckoutDiscountCodeApplyV2PayloadQuery ->
payloadQuery
.checkout { checkoutQuery: CheckoutQuery ->
checkoutQuery.checkoutFieldsFragment()
}
.checkoutUserErrors() { userErrorQuery: CheckoutUserErrorQuery ->
.checkoutUserErrors { userErrorQuery: CheckoutUserErrorQuery ->
userErrorQuery
.field()
.message()
@ -183,44 +179,9 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
val query = mutation { mutationQuery: MutationQuery ->
mutationQuery
.checkoutDiscountCodeRemove(
checkoutID
checkoutID,
) { payloadQuery: CheckoutDiscountCodeRemovePayloadQuery ->
payloadQuery
.checkout { checkoutQuery: CheckoutQuery ->
checkoutQuery.checkoutFieldsFragment()
}
.checkoutUserErrors() { userErrorQuery: CheckoutUserErrorQuery ->
userErrorQuery
.field()
.message()
}
}
}
return runCheckoutMutation(query)
}
suspend fun updateAddress(
address: MailingAddress,
checkoutID: ID,
waitForShippingRates: Boolean
): Result<Checkout> {
val input = MailingAddressInput()
.setAddress1(address.address1)
.setAddress2(address.address2)
.setCity(address.city)
.setCountry(address.country)
.setFirstName(address.firstName)
.setLastName(address.lastName)
.setPhone(address.phone)
.setProvince(address.province)
.setZip(address.zip)
val query = mutation { mutationQuery: MutationQuery ->
mutationQuery
.checkoutShippingAddressUpdateV2(
input, checkoutID
) { shippingAddressUpdatePayloadQuery: CheckoutShippingAddressUpdateV2PayloadQuery ->
shippingAddressUpdatePayloadQuery
.checkout { checkoutQuery: CheckoutQuery ->
checkoutQuery.checkoutFieldsFragment()
}
@ -231,59 +192,18 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
}
}
}
return runCheckoutMutation(query)
}
suspend fun updateEmail(email: String?, checkoutID: ID): Result<Checkout> {
val query = mutation { mutationQuery: MutationQuery ->
mutationQuery
.checkoutEmailUpdateV2(
checkoutID, email
) { emailUpdatePayloadQuery: CheckoutEmailUpdateV2PayloadQuery ->
emailUpdatePayloadQuery
.checkout { checkoutQuery: CheckoutQuery ->
checkoutQuery.checkoutFieldsFragment()
}
.checkoutUserErrors() { userErrorQuery: CheckoutUserErrorQuery ->
userErrorQuery
.field()
.message()
}
}
}
return runCheckoutMutation(query)
}
suspend fun updateShippingRate(handle: String?, checkoutID: ID): Result<Checkout> {
val query = mutation { mutationQuery: MutationQuery ->
mutationQuery
.checkoutShippingLineUpdate(
checkoutID, handle
) { shippingLineUpdatePayloadQuery: CheckoutShippingLineUpdatePayloadQuery ->
shippingLineUpdatePayloadQuery
.checkout { checkoutQuery: CheckoutQuery ->
checkoutQuery.checkoutFieldsFragment()
}
.checkoutUserErrors() { userErrorQuery: CheckoutUserErrorQuery ->
userErrorQuery
.field()
.message()
}
}
}
return runCheckoutMutation(query)
}
suspend fun completeWithTokenizedPayment(
payment: TokenizedPaymentInputV3,
checkoutID: ID
checkoutID: ID,
): Result<Checkout> {
val query = mutation { mutationQuery: MutationQuery ->
mutationQuery
.checkoutCompleteWithTokenizedPaymentV3(
checkoutID, payment
checkoutID, payment,
) { payloadQuery: CheckoutCompleteWithTokenizedPaymentV3PayloadQuery ->
payloadQuery
.payment { paymentQuery: PaymentQuery ->
@ -295,7 +215,7 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
checkoutQuery
.ready()
}
.checkoutUserErrors() { userErrorQuery: CheckoutUserErrorQuery ->
.checkoutUserErrors { userErrorQuery: CheckoutUserErrorQuery ->
userErrorQuery
.field()
.message()
@ -305,10 +225,6 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
return runCheckoutMutation(query)
}
fun startGooglePaySession() {
// PaySession()
}
private suspend fun runCheckoutMutation(mutation: MutationQuery): Result<Checkout> {
return when (val result = mutationQueryAsync(mutation)) {
is GraphCallResult.Success -> {
@ -322,13 +238,13 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
Result.success(checkout)
}
is GraphCallResult.Failure -> Result.failure(result.error)
is GraphCallResult.Failure -> Result.failure(result.error)
}
}
private suspend fun queryAsync(
query: QueryRootQuery,
retryHandler: RetryHandler<QueryRoot>
retryHandler: RetryHandler<QueryRoot>,
): GraphCallResult<QueryRoot> =
withContext(Dispatchers.IO) {
suspendCoroutine { continuation ->
@ -358,12 +274,11 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
}
}
private fun initClient(): GraphClient {
return GraphClient.build(
application,
shop.domain,
shop.storefrontApiKey
shop.storefrontApiKey,
) {
// httpCache(application.filesDir) {
// cacheMaxSizeBytes = (1024 * 1024 * 10)
@ -371,13 +286,9 @@ class ShopifyService(private val application: Application, val shop: ShopifyShop
// HttpCachePolicy.Default.CACHE_FIRST.expireAfter(20, TimeUnit.MINUTES)
// }
}
}
}
sealed class ShopifyError : Throwable() {
object Unknown : ShopifyError()
object GooglePayFailed : ShopifyError()
class UserError(val errorMessage: String)
}

View file

@ -2,23 +2,24 @@ package com.tangem.tap.common.shop.shopify.data
import com.shopify.buy3.Storefront
@Suppress("LongMethod", "MagicNumber")
fun Storefront.CheckoutQuery.checkoutFieldsFragment() {
// id()
ready()
webUrl()
currencyCode()
lineItemsSubtotalPrice { it.amount() }
totalPriceV2 {
it.currencyCode()
it.amount() }
it.amount()
}
lineItems({ arg -> arg.first(250) }) {
it.edges {
it.node {
// it.id()
it.title()
it.quantity()
it.variant() {
it.priceV2() { it.amount() }
it.variant {
it.priceV2 { it.amount() }
}
}
}

View file

@ -2,13 +2,15 @@ package com.tangem.tap.common.shop.shopify.data
import com.shopify.buy3.Storefront
@Suppress("MagicNumber")
fun Storefront.CollectionConnectionQuery.collectionFieldsFragment() {
edges { collectionEdgeQuery ->
collectionEdgeQuery
.node { collectionQuery ->
collectionQuery
.title()
.products({ arg -> arg.first(250) }
.products(
{ arg -> arg.first(250) },
) { productConnectionQuery ->
productConnectionQuery
.edges { productEdgeQuery ->

View file

@ -8,13 +8,22 @@ import java.util.regex.Pattern
[REDACTED_AUTHOR]
*/
class DecimalDigitsInputFilter(
digitsBeforeDecimal: Int,
digitsAfterDecimal: Int,
private val decimalSeparator: String
digitsBeforeDecimal: Int,
digitsAfterDecimal: Int,
private val decimalSeparator: String,
) : InputFilter {
private val pattern: Pattern = Pattern.compile("(([1-9]{1}[0-9]{0,${digitsBeforeDecimal - 1}})?||[0]{1})((\\$decimalSeparator[0-9]{0,$digitsAfterDecimal})?)||(\\$decimalSeparator)?")
@Suppress("MaxLineLength")
private val pattern: Pattern =
Pattern.compile("(([1-9]{1}[0-9]{0,${digitsBeforeDecimal - 1}})?||[0]{1})((\\$decimalSeparator[0-9]{0,$digitsAfterDecimal})?)||(\\$decimalSeparator)?")
override fun filter(source: CharSequence, sourceStart: Int, sourceEnd: Int, destination: Spanned, destinationStart: Int, destinationEnd: Int): CharSequence? {
override fun filter(
source: CharSequence,
sourceStart: Int,
sourceEnd: Int,
destination: Spanned,
destinationStart: Int,
destinationEnd: Int,
): CharSequence? {
val destString = destination.toString()
val prefix = destString.substring(0, destinationStart)
val suffix = destString.substring(destinationEnd, destString.length)

View file

@ -17,6 +17,7 @@ import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
@Suppress("MagicNumber")
class RefreshBalanceWidget(
private val root: ViewGroup,
) : ViewStateWidget {
@ -57,6 +58,7 @@ class RefreshBalanceWidget(
viewSwitcher.showNext()
}
@Suppress("MagicNumber")
private fun animateState(state: ProgressState) {
when (state) {
ProgressState.Done, ProgressState.Error -> {
@ -94,6 +96,7 @@ class RefreshBalanceWidget(
private fun getState(): ProgressState = if (isArrowRefreshActive()) ProgressState.Done else ProgressState.Loading
}
@Suppress("MagicNumber")
class ShowAnimation : AnimationSet(true) {
init {
addAnimation(

View file

@ -2,7 +2,6 @@ package com.tangem.tap.common.toggleWidget
import android.view.View
/**
[REDACTED_AUTHOR]
*/

View file

@ -6,54 +6,50 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
import com.tangem.wallet.R
class SimpleAlertDialog {
companion object {
fun create(
titleRes: Int? = null,
messageRes: Int? = null,
title: String? = null,
message: String? = null,
primaryButtonRes: Int = R.string.common_ok,
context: Context,
): AlertDialog {
return SimpleCancelableAlertDialog.create(
titleRes = titleRes,
messageRes = messageRes,
title = title,
message = message,
primaryButtonRes = primaryButtonRes,
secondaryButtonRes = null,
context = context
)
}
object SimpleAlertDialog {
fun create(
titleRes: Int? = null,
messageRes: Int? = null,
title: String? = null,
message: String? = null,
primaryButtonRes: Int = R.string.common_ok,
context: Context,
): AlertDialog {
return SimpleCancelableAlertDialog.create(
titleRes = titleRes,
messageRes = messageRes,
title = title,
message = message,
primaryButtonRes = primaryButtonRes,
secondaryButtonRes = null,
context = context,
)
}
}
class SimpleCancelableAlertDialog {
companion object {
fun create(
titleRes: Int? = null,
messageRes: Int? = null,
title: String? = null,
message: String? = null,
primaryButtonRes: Int = R.string.common_ok,
secondaryButtonRes: Int? = R.string.common_cancel,
primaryButtonAction: () -> Unit = {},
secondaryButtonAction: () -> Unit = {},
context: Context,
): AlertDialog {
object SimpleCancelableAlertDialog {
fun create(
titleRes: Int? = null,
messageRes: Int? = null,
title: String? = null,
message: String? = null,
primaryButtonRes: Int = R.string.common_ok,
secondaryButtonRes: Int? = R.string.common_cancel,
primaryButtonAction: () -> Unit = {},
secondaryButtonAction: () -> Unit = {},
context: Context,
): AlertDialog {
return AlertDialog.Builder(context).apply {
setTitle(titleRes?.let { context.getString(it) } ?: title )
setMessage(messageRes?.let { context.getString(it) } ?: message)
setPositiveButton(context.getText(primaryButtonRes)) { _, _ -> primaryButtonAction() }
if (secondaryButtonRes != null) {
setNegativeButton(context.getText(secondaryButtonRes)) { _, _ -> secondaryButtonAction()}
}
setOnDismissListener {
store.dispatch(GlobalAction.HideDialog)
}
}.create()
}
return AlertDialog.Builder(context).apply {
setTitle(titleRes?.let { context.getString(it) } ?: title)
setMessage(messageRes?.let { context.getString(it) } ?: message)
setPositiveButton(context.getText(primaryButtonRes)) { _, _ -> primaryButtonAction() }
if (secondaryButtonRes != null) {
setNegativeButton(context.getText(secondaryButtonRes)) { _, _ -> secondaryButtonAction() }
}
setOnDismissListener {
store.dispatch(GlobalAction.HideDialog)
}
}.create()
}
}

View file

@ -10,7 +10,11 @@ import java.util.*
class PayIdManager {
suspend fun verifyPayId(payId: String, blockchain: Blockchain): Result<VerifyPayIdResponse> = withContext(Dispatchers.IO) {
@Suppress("MagicNumber")
suspend fun verifyPayId(
payId: String,
blockchain: Blockchain,
): Result<VerifyPayIdResponse> = withContext(Dispatchers.IO) {
val splitPayId = payId.split("\$")
val user = splitPayId[0]
val baseUrl = "https://${splitPayId[1]}/"
@ -19,28 +23,30 @@ class PayIdManager {
private fun Blockchain.getPayIdNetwork(): String {
return when (this) {
Blockchain.XRP -> "XRPL"
Blockchain.RSK -> "RSK"
else -> this.currency
}.toLowerCase()
Blockchain.XRP -> "XRPL"
Blockchain.RSK -> "RSK"
else -> this.currency
}.lowercase(Locale.getDefault())
}
companion object {
val payIdRegExp = "^[a-z0-9!#@%&*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#@%&*+/=?^_`{|}~-]+)*\\\$(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z-]*[a-z0-9])?|(?:[0-9]{1,3}\\.){3}[0-9]{1,3})\$".toRegex()
@Suppress("MaxLineLength")
val payIdRegExp =
"^[a-z0-9!#@%&*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#@%&*+/=?^_`{|}~-]+)*\\\$(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z-]*[a-z0-9])?|(?:[0-9]{1,3}\\.){3}[0-9]{1,3})\$".toRegex()
val payIdSupported: EnumSet<Blockchain> = EnumSet.of(
Blockchain.XRP,
Blockchain.Ethereum,
Blockchain.Bitcoin,
Blockchain.Litecoin,
Blockchain.Stellar,
Blockchain.Cardano,
Blockchain.CardanoShelley,
Blockchain.Ducatus,
Blockchain.BitcoinCash,
Blockchain.Binance,
Blockchain.RSK,
Blockchain.Tezos
Blockchain.XRP,
Blockchain.Ethereum,
Blockchain.Bitcoin,
Blockchain.Litecoin,
Blockchain.Stellar,
Blockchain.Cardano,
Blockchain.CardanoShelley,
Blockchain.Ducatus,
Blockchain.BitcoinCash,
Blockchain.Binance,
Blockchain.RSK,
Blockchain.Tezos,
)
fun isPayId(value: String?): Boolean = value?.contains(payIdRegExp) ?: false

View file

@ -42,7 +42,7 @@ class RatesRepository(
}
.onFailure { Result.Failure(it) }
throw IllegalStateException("Unreachable code because runCatching must return result")
error("Unreachable code because runCatching must return result")
}
private fun handleFiatRatesResult(rates: Map<Currency, Result<BigDecimal>?>): Result.Success<RatesResult> {

View file

@ -21,6 +21,7 @@ import com.tangem.common.extensions.ByteArrayKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.common.map
import com.tangem.common.usersCode.UserCodeRepository
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.ScanResponse
import com.tangem.operations.CommandResponse
@ -30,7 +31,6 @@ import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
import com.tangem.operations.pins.CheckUserCodesCommand
import com.tangem.operations.pins.CheckUserCodesResponse
import com.tangem.operations.pins.SetUserCodeCommand
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
@ -211,6 +211,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return withContext(Dispatchers.Main) { result }
}
@Suppress("MagicNumber")
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
tangemSdk.config.cardIdDisplayFormat = when {
scanResponse == null -> CardIdDisplayFormat.Full

View file

@ -17,21 +17,20 @@ interface MultiMessageError : TapErrors {
sealed class TapError(
@StringRes val messageResource: Int,
override val args: List<Any>? = null
override val args: List<Any>? = null,
) : Throwable(), TapErrors, ArgError {
object UnknownError : TapError(R.string.send_error_unknown)
open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage))
object ScanCardError : TapError(R.string.scan_card_error)
object PayIdAlreadyCreated : TapError(R.string.wallet_create_payid_error_already_created)
object PayIdCreatingError : TapError(R.string.wallet_create_payid_error_message)
object PayIdEmptyField : TapError(R.string.wallet_create_payid_empty)
object UnknownBlockchain : TapError(R.string.wallet_error_unsupported_blockchain_subtitle)
object NoInternetConnection : TapError(R.string.wallet_notification_no_internet)
object InsufficientBalance : TapError(R.string.send_error_insufficient_balance)
object BlockchainInternalError : TapError(R.string.send_error_blockchain_internal)
object AmountExceedsBalance : TapError(R.string.send_validation_amount_exceeds_balance)
data class AmountLowerExistentialDeposit(override val args: List<Any>) : TapError(R.string.send_error_minimum_balance_format)
data class AmountLowerExistentialDeposit(
override val args: List<Any>,
) : TapError(R.string.send_error_minimum_balance_format)
object FeeExceedsBalance : TapError(R.string.send_validation_invalid_fee)
object TotalExceedsBalance : TapError(R.string.send_validation_invalid_total)
object InvalidAmountValue : TapError(R.string.send_validation_invalid_amount)
@ -48,7 +47,6 @@ sealed class TapError(
object CreationError : CustomError("Can't create wallet manager")
class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount)
class InternalError(message: String) : CustomError(message)
object BlockchainIsUnreachable : TapError(R.string.wallet_balance_blockchain_unreachable)
object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later)
}

View file

@ -77,6 +77,7 @@ class ConfigManager {
)
}
@Suppress("LongMethod")
private fun setupKey(configValues: ConfigValueModel?) {
val values = configValues ?: return
config = config.copy(

View file

@ -15,6 +15,7 @@ class FeatureModel(
val isCreatingTwinCardsAllowed: Boolean,
)
@Suppress("LongParameterList")
class ConfigValueModel(
val coinMarketCapKey: String,
val mercuryoWidgetId: String,

View file

@ -70,6 +70,8 @@ class WarningMessagesManager {
}
companion object {
const val REMAINING_SIGNATURES_WARNING = 10
fun devCardWarning(): WarningMessage = WarningMessage(
"",
"",
@ -171,7 +173,5 @@ class WarningMessagesManager {
R.string.alert_demo_message,
WarningMessage.Origin.Local,
)
const val REMAINING_SIGNATURES_WARNING = 10
}
}

View file

@ -5,7 +5,7 @@ import com.tangem.blockchain.common.Token
import com.tangem.common.card.EllipticCurve
import java.math.BigDecimal
@Suppress("MagicNumber")
fun Blockchain.amountToCreateAccount(token: Token? = null): BigDecimal? {
return when (this) {
Blockchain.Stellar -> if (token?.symbol == NODL) BigDecimal(1.5) else BigDecimal.ONE

View file

@ -37,6 +37,7 @@ val CardDTO.isTangemWallet: Boolean
&& firmwareVersion >= FirmwareVersion.MultiWalletAvailable
&& !isSaltPay
@Suppress("UnnecessaryParentheses")
fun CardDTO.hasSignedHashes(): Boolean {
return wallets.any { (it.totalSignedHashes ?: 0) > 0 }
}

View file

@ -45,7 +45,7 @@ fun WalletManagerFactory.makeWalletManagerForApp(
curve = wallet.curve,
)
}
scanResponse.card.isHdWalletAllowedByApp && (seedKey != null && derivationParams != null) -> {
scanResponse.card.isHdWalletAllowedByApp && seedKey != null && derivationParams != null -> {
val derivedKeys = scanResponse.derivedKeys[wallet.publicKey.toMapKey()]
val derivationPath = when (derivationParams) {
is DerivationParams.Default -> blockchain.derivationPath(derivationParams.style)
@ -143,12 +143,13 @@ fun WalletManagerFactory.makeSaltPayWalletManager(
scanResponse: ScanResponse,
): EthereumWalletManager {
val blockchain = scanResponse.getBlockchain()
if (blockchain != Blockchain.SaltPay)
throw IllegalArgumentException("WalletManager for the SaltPay can be created based only on Blockchain.SaltPay")
if (blockchain != Blockchain.SaltPay) {
error("WalletManager for the SaltPay can be created based only on Blockchain.SaltPay")
}
val token = SaltPayWorkaround.tokenFrom(blockchain)
val cardWallet = scanResponse.card.wallets.firstOrNull().guard {
throw NullPointerException("SaltPay card must have one wallet at least")
error("SaltPay card must have one wallet at least")
}
return makeWalletManager(

View file

@ -57,7 +57,7 @@ private class BlockchainNetworkWalletStoreBuilderImpl(
userWalletId = userWalletId,
blockchain = blockchainNetwork.blockchain,
derivationPath = blockchainNetwork.derivationPath?.let { DerivationPath(it) },
walletsData = (listOf(blockchainWalletData) + tokensWalletsData),
walletsData = listOf(blockchainWalletData) + tokensWalletsData,
walletRent = null,
walletManager = walletManager,
blockchainNetwork = blockchainNetwork,
@ -79,7 +79,7 @@ private class WalletMangerWalletStoreBuilderImpl(
userWalletId = userWalletId,
blockchain = wallet.blockchain,
derivationPath = wallet.publicKey.derivationPath,
walletsData = (listOf(blockchainWalletData) + listOfNotNull(tokenWalletsData)),
walletsData = listOf(blockchainWalletData) + listOfNotNull(tokenWalletsData),
walletRent = null,
walletManager = walletManager,
blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager),

View file

@ -15,8 +15,9 @@ internal class AddCustomTokenErrorConverter(
private val context: Context,
) : ModuleMessageConverter<DomainModuleError, ConvertedMessage?> {
@Suppress("MagicNumber")
override fun convert(message: DomainModuleError): ConvertedMessage? {
val customTokenError = (message as? AddCustomTokenError) ?: throw UnsupportedOperationException()
val customTokenError = message as? AddCustomTokenError ?: throw UnsupportedOperationException()
val rawMessage = when (customTokenError) {
AddCustomTokenError.Warning.PotentialScamToken -> R.string.custom_token_validation_error_not_found

View file

@ -14,7 +14,7 @@ internal class SaltPayActivationErrorConverter(
) : ModuleMessageConverter<SaltPayActivationError, ConvertedDialogMessage?> {
override fun convert(message: SaltPayActivationError): ConvertedDialogMessage? {
val saltPayError = (message as? SaltPayActivationError) ?: throw UnsupportedOperationException()
val saltPayError = message as? SaltPayActivationError ?: throw UnsupportedOperationException()
val dialogMessage = when (saltPayError) {
SaltPayActivationError.NoGas -> {

View file

@ -6,12 +6,12 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.services.Result
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.backup.BackupService
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.backupService
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor
import com.tangem.tap.common.extensions.dispatchDialogShow
@ -141,31 +141,34 @@ object ScanCardProcessor {
store.dispatchOnMain(DisclaimerAction.SetDisclaimer(disclaimer))
if (disclaimer.isAccepted()) {
nextHandler((scanResponse))
} else scope.launch {
delay(DELAY_SDK_DIALOG_CLOSE)
disclaimerWillShow()
dispatchOnMain(
DisclaimerAction.Show(
fromScreen = AppScreen.Home,
callback = DisclaimerCallback(
onAccept = {
scope.launch(Dispatchers.Main) {
nextHandler(scanResponse)
}
},
onDismiss = {
scope.launch(Dispatchers.Main) {
onProgressStateChange(false)
onFailure(TangemSdkError.UserCancelled())
}
},
nextHandler(scanResponse)
} else {
scope.launch {
delay(DELAY_SDK_DIALOG_CLOSE)
disclaimerWillShow()
dispatchOnMain(
DisclaimerAction.Show(
fromScreen = AppScreen.Home,
callback = DisclaimerCallback(
onAccept = {
scope.launch(Dispatchers.Main) {
nextHandler(scanResponse)
}
},
onDismiss = {
scope.launch(Dispatchers.Main) {
onProgressStateChange(false)
onFailure(TangemSdkError.UserCancelled())
}
},
),
),
),
)
)
}
}
}
@Suppress("LongMethod", "MagicNumber")
private suspend inline fun onScanSuccess(
scanResponse: ScanResponse,
crossinline onProgressStateChange: suspend (showProgress: Boolean) -> Unit,

View file

@ -56,6 +56,7 @@ class BackupStepAdapter {
}
}
@Suppress("MagicNumber")
@FromJson
fun fromJson(json: String): BackupStep {
return when (json) {

View file

@ -1,55 +0,0 @@
//package com.tangem.tap.domain.tasks
//
//import com.tangem.common.CompletionResult
//import com.tangem.common.core.CardSession
//import com.tangem.common.core.CardSessionRunnable
//import com.tangem.common.core.CompletionCallback
//import com.tangem.common.hdWallet.DerivationPath
//import com.tangem.common.hdWallet.ExtendedPublicKey
//import com.tangem.operations.CommandResponse
//import com.tangem.operations.derivation.DeriveWalletPublicKeysTask
//import com.tangem.tap.common.extensions.ByteArrayKey
//
//class ExtendedPublicKeyList(
// items: Collection<ExtendedPublicKey>
//): ArrayList<ExtendedPublicKey>(items), CommandResponse
//
//
//class DerivationTaskResponse(
// val entries: Map<ByteArrayKey, ExtendedPublicKeyList>
//): CommandResponse
//
//class DerivationTask(
// private val derivations: Map<ByteArrayKey, List<DerivationPath>>
//) : CardSessionRunnable<DerivationTaskResponse> {
//
// val response: MutableMap<ByteArrayKey, ExtendedPublicKeyList> = mutableMapOf()
//
// override fun run(session: CardSession, callback: CompletionCallback<DerivationTaskResponse>) {
// derive(keys = derivations.keys.toList(), index = 0, session = session, callback = callback)
// }
//
// private fun derive(
// keys: List<ByteArrayKey>,
// index: Int,
// session: CardSession,
// callback: CompletionCallback<DerivationTaskResponse>
// ) {
// if (index == keys.count()) {
// callback(CompletionResult.Success(DerivationTaskResponse(response.toMap())))
// return
// }
//
// val key = keys[index]
// val paths = derivations[key]!!
// DeriveWalletPublicKeysTask(key.bytes, paths).run(session) { result ->
// when (result) {
// is CompletionResult.Success -> {
// response[key] = result.data[key]
// derive(keys = keys, index = index + 1, session = session, callback = callback)
// }
// is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error))
// }
// }
// }
//}

View file

@ -8,7 +8,7 @@ import com.tangem.common.extensions.guard
import com.tangem.operations.backup.ResetBackupCommand
import com.tangem.operations.wallet.PurgeWalletCommand
class ResetToFactorySettingsTask() : CardSessionRunnable<Card> {
class ResetToFactorySettingsTask : CardSessionRunnable<Card> {
override fun run(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
deleteWallets(session, callback)

View file

@ -170,7 +170,8 @@ private class ScanWalletProcessor(
) {
val activationInProgress = preferencesStorage.usedCardsPrefStorage.isActivationInProgress(card.cardId)
if ((card.backupStatus == CardDTO.BackupStatus.NoBackup && card.wallets.isNotEmpty())
@Suppress("ComplexCondition")
if (card.backupStatus == CardDTO.BackupStatus.NoBackup && card.wallets.isNotEmpty()
&& (activationInProgress || card.isSaltPay)
) {
StartPrimaryCardLinkingTask().run(session) { linkingResult ->
@ -266,7 +267,7 @@ private class ScanWalletProcessor(
}
if (additionalBlockchainsToDerive != null) {
blockchainsToDerive.addAll(
additionalBlockchainsToDerive.map { BlockchainNetwork(it, card) }
additionalBlockchainsToDerive.map { BlockchainNetwork(it, card) },
)
}
if (!card.useOldStyleDerivation) {
@ -307,6 +308,7 @@ private class ScanWalletProcessor(
}
}
@Suppress("MagicNumber")
private class ScanTwinProcessor : ProductCommandProcessor<ScanResponse> {
override fun proceed(
card: CardDTO,

View file

@ -43,7 +43,7 @@ class LoadAvailableCoinsService(
LoadedCoins(
currencies = data.coins.map { Currency.fromCoinResponse(it, data.imageHost) },
moreAvailable = data.total > offset + LOAD_PER_PAGE,
)
),
)
}
is Result.Failure -> {
@ -70,7 +70,7 @@ class LoadAvailableCoinsService(
.onSuccess { return@withContext Result.Success(it) }
.onFailure { return@withContext Result.Failure(it) }
throw IllegalStateException("Unreachable code because runCatching must return result")
error("Unreachable code because runCatching must return result")
}
}

View file

@ -119,7 +119,7 @@ class OldUserTokensRepository(
.onSuccess { return@map it.coins.firstOrNull()?.id }
.onFailure { return@map null }
throw IllegalStateException("Unreachable code because runCatching must return result")
error("Unreachable code because runCatching must return result")
}
.mapIndexedNotNull { index, id ->
if (id == null) null else tokens[index].contractAddress to id

View file

@ -49,7 +49,7 @@ class UserTokensRepository(
return@withContext handleGetUserTokensFailure(card = card, userId = userId, error = it)
}
throw IllegalStateException("Unreachable code because runCatching must return result")
error("Unreachable code because runCatching must return result")
}
// TODO("After adding DI") replace with CoroutineDispatcherProvider

View file

@ -10,14 +10,13 @@ data class BlockchainDao(
@Json(name = "key")
val name: String,
@Json(name = "testnet")
val isTestNet: Boolean
val isTestNet: Boolean,
) {
@Deprecated("The method is used only for migration from older versions of the app")
fun toBlockchain(): Blockchain {
val blockchain = Blockchain.values().find { it.name.lowercase() == name.lowercase() }
?: throw Exception("Invalid BlockchainDao")
return if (!isTestNet) blockchain else blockchain.getTestnetVersion()
?: throw Exception("Invalid BlockchainDao")
?: error("Blockchain is null")
return if (!isTestNet) blockchain else blockchain.getTestnetVersion() ?: error("Invalid BlockchainDao")
}
companion object {

View file

@ -12,15 +12,16 @@ import com.tangem.domain.common.TapWorkarounds.derivationStyle
data class BlockchainNetwork(
val blockchain: Blockchain,
val derivationPath: String?,
val tokens: List<Token>
val tokens: List<Token>,
) {
constructor(
blockchain: Blockchain,
card: CardDTO,
) : this(
constructor(blockchain: Blockchain, card: CardDTO) : this(
blockchain = blockchain,
derivationPath = if (card.settings.isHDWalletAllowed) blockchain.derivationPath(card.derivationStyle)?.rawPath else null,
derivationPath = if (card.settings.isHDWalletAllowed) {
blockchain.derivationPath(card.derivationStyle)?.rawPath
} else {
null
},
tokens = emptyList(),
)
@ -43,7 +44,7 @@ data class BlockchainNetwork(
}
override fun hashCode(): Int = calculateHashCode(
blockchain.hashCode(), derivationPath?.hashCode() ?: 0
blockchain.hashCode(), derivationPath?.hashCode() ?: 0,
)
companion object {
@ -51,7 +52,7 @@ data class BlockchainNetwork(
return BlockchainNetwork(
walletManager.wallet.blockchain,
walletManager.wallet.publicKey.derivationPath?.rawPath,
walletManager.cardTokens.toList()
walletManager.cardTokens.toList(),
)
}
}

View file

@ -11,14 +11,13 @@ import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapView
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapViewState
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
/**
[REDACTED_AUTHOR]
*/
class TwinsCardWidget(
val leapfrogWidget: LeapfrogWidget,
private val deviceScaleFactor: Float = 1f,
val getTopOfAnchorViewForActivateState: () -> Float
val getTopOfAnchorViewForActivateState: () -> Float,
) {
var state: TwinCardsWidgetState? = null
@ -37,7 +36,7 @@ class TwinsCardWidget(
val animator = createAnimator(animate)
animator.playTogether(
createAnimator(TwinCardNumber.First, createWelcomeProperties(TwinCardNumber.First)),
createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second))
createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second)),
)
animator.doOnEnd { onEnd() }
leapfrogWidget.fold(animate) { animator.start() }
@ -50,7 +49,7 @@ class TwinsCardWidget(
val animator = createAnimator(animate)
animator.playTogether(
createAnimator(TwinCardNumber.First, createLeapfrogProperties(TwinCardNumber.First)),
createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second))
createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second)),
)
animator.doOnEnd {
leapfrogWidget.initViews()
@ -66,7 +65,7 @@ class TwinsCardWidget(
val animator = createAnimator(animate)
animator.playTogether(
createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)),
createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second))
createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second)),
)
animator.doOnEnd { onEnd() }
animator.start()
@ -85,6 +84,7 @@ class TwinsCardWidget(
return animator
}
@Suppress("MagicNumber")
private fun createWelcomeProperties(cardNumber: TwinCardNumber): TwinsCardProperties {
return when (cardNumber) {
TwinCardNumber.First -> {
@ -118,6 +118,7 @@ class TwinsCardWidget(
)
}
@Suppress("MagicNumber")
private fun createActivateProperties(cardNumber: TwinCardNumber): TwinsCardProperties {
val topOfAnchorView = getTopOfAnchorViewForActivateState()
val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state)

View file

@ -34,11 +34,14 @@ class WriteProtectedIssuerDataTask(
is CompletionResult.Success -> {
writeIssuerData(
twinPublicKey, issuerKeys, signResult.data.signature,
readResult.data, session, callback
readResult.data, session, callback,
)
}
is CompletionResult.Failure -> callback(CompletionResult.Failure(
readResult.error))
is CompletionResult.Failure -> callback(
CompletionResult.Failure(
readResult.error,
),
)
}
}
}
@ -47,6 +50,7 @@ class WriteProtectedIssuerDataTask(
}
}
@Suppress("LongParameterList")
private fun writeIssuerData(
twinPublicKey: ByteArray, issuerKeys: KeyPair, cardSignature: ByteArray,
readResponse: ReadIssuerDataResponse,

View file

@ -21,10 +21,10 @@ interface UserWalletsListManager {
/**
* Save provided user wallet and set it as selected
* @param userWallet [UserWallet] to save
* @param canOverride If false, then terminate with [UserWalletListError.WalletAlreadySaved] when user tries to save an
* already saved card
* @param canOverride If false, then terminate with [UserWalletListError.WalletAlreadySaved] when user tries
* to save an already saved card
* @return [CompletionResult] of operation
* */
*/
suspend fun save(userWallet: UserWallet, canOverride: Boolean = false): CompletionResult<Unit>
/**

View file

@ -219,13 +219,18 @@ internal class BiometricUserWalletsListManager(
private suspend fun loadModels(): CompletionResult<Unit> {
return getSavedUserWallets()
.map { userWallets ->
if (userWallets.isNotEmpty()) state.update { prevState ->
val wallets = (userWallets + prevState.userWallets).distinctBy { it.walletId }
if (userWallets.isNotEmpty()) {
state.update { prevState ->
val wallets = (userWallets + prevState.userWallets).distinctBy { it.walletId }
prevState.copy(
userWallets = wallets,
selectedUserWalletId = findOrSetSelectedUserWalletId(prevState.selectedUserWalletId, wallets),
)
prevState.copy(
userWallets = wallets,
selectedUserWalletId = findOrSetSelectedUserWalletId(
prevSelectedWalletId = prevState.selectedUserWalletId,
userWallets = wallets,
),
)
}
}
}
.doOnFailure { error ->

View file

@ -134,7 +134,7 @@ internal class BiometricUserWalletsKeysRepository(
}
private suspend fun deleteUserWalletsIds(userWalletsIds: List<UserWalletId>) {
val remainingIds = (getUserWalletsIds() - userWalletsIds.toSet())
val remainingIds = getUserWalletsIds() - userWalletsIds.toSet()
withContext(Dispatchers.IO) {
secureStorage.store(remainingIds.encode(), StorageKey.UserWalletIds.name)

View file

@ -11,6 +11,7 @@ sealed class WalletStoresError(code: Int) : TangemError(code) {
override val message: String?
get() = customMessage
@Suppress("MagicNumber")
class FetchFiatRatesError(
currencies: List<String>,
override val cause: Throwable?,
@ -18,18 +19,22 @@ sealed class WalletStoresError(code: Int) : TangemError(code) {
override var customMessage: String = "Failed to fetch fiat rates for currencies $currencies"
}
@Suppress("MagicNumber")
class UnknownBlockchain : WalletStoresError(60012) {
override var customMessage: String = "Unknown blockchain"
}
@Suppress("MagicNumber")
object NoInternetConnection : WalletStoresError(60013) {
override var customMessage: String = "No internet connection"
}
@Suppress("MagicNumber")
class WalletManagerNotCreated(blockchain: Blockchain) : WalletStoresError(60014) {
override var customMessage: String = "Wallet manager can not be created for $blockchain"
}
@Suppress("MagicNumber")
class UpdateWalletManagerError(
blockchain: Blockchain,
override val cause: Throwable,

View file

@ -48,6 +48,7 @@ import kotlinx.coroutines.withContext
import timber.log.Timber
import java.math.BigDecimal
@Suppress("LargeClass")
internal class DefaultWalletAmountsRepository(
private val tangemTechApi: TangemTechApi,
private val dispatchers: CoroutineDispatcherProvider,
@ -59,13 +60,16 @@ internal class DefaultWalletAmountsRepository(
userWallets: List<UserWallet>,
fiatCurrency: FiatCurrency,
): CompletionResult<Unit> {
return if (userWallets.isEmpty()) CompletionResult.Success(Unit)
else withContext(Dispatchers.Default) {
awaitAll(
async { fetchAmountsForUserWallets(userWallets) },
async { fetchFiatRates(userWallets, walletStores = null, fiatCurrency) },
)
.fold()
return if (userWallets.isEmpty()) {
CompletionResult.Success(Unit)
} else {
withContext(Dispatchers.Default) {
awaitAll(
async { fetchAmountsForUserWallets(userWallets) },
async { fetchFiatRates(userWallets, walletStores = null, fiatCurrency) },
)
.fold()
}
}
}
@ -81,16 +85,19 @@ internal class DefaultWalletAmountsRepository(
userWallet: UserWallet,
fiatCurrency: FiatCurrency,
): CompletionResult<Unit> {
return if (walletStores.isEmpty()) CompletionResult.Success(Unit)
else withContext(Dispatchers.Default) {
val userWalletId = userWallet.walletId
val scanResponse = userWallet.scanResponse
return if (walletStores.isEmpty()) {
CompletionResult.Success(Unit)
} else {
withContext(Dispatchers.Default) {
val userWalletId = userWallet.walletId
val scanResponse = userWallet.scanResponse
awaitAll(
async { fetchAmountForWalletStores(userWalletId, scanResponse, walletStores) },
async { fetchFiatRates(listOf(userWallet), walletStores, fiatCurrency) },
)
.fold()
awaitAll(
async { fetchAmountForWalletStores(userWalletId, scanResponse, walletStores) },
async { fetchFiatRates(listOf(userWallet), walletStores, fiatCurrency) },
)
.fold()
}
}
}
@ -139,7 +146,7 @@ internal class DefaultWalletAmountsRepository(
return@withContext CompletionResult.Failure(error)
}
throw IllegalStateException("Unreachable code because runCatching must return result")
error("Unreachable code because runCatching must return result")
}
}
@ -168,7 +175,7 @@ internal class DefaultWalletAmountsRepository(
): CompletionResult<Unit> = coroutineScope {
walletStores.map { walletStore ->
async {
// TODO: Find wallet manager via [com.tangem.tap.domain.walletStores.repository.WalletManagersRepository]
//TODO: Find wallet manager via [com.tangem.tap.domain.walletStores.repository.WalletManagersRepository]
val walletManager = walletStore.walletManager
fetchAmountsForWalletStore(userWalletId, scanResponse, walletStore, walletManager)
}
@ -408,8 +415,10 @@ internal class DefaultWalletAmountsRepository(
val error = WalletStoresError.NoInternetConnection
Timber.e(error)
CompletionResult.Failure(error)
} else withContext(Dispatchers.IO) {
catching { block() }
} else {
withContext(Dispatchers.IO) {
catching { block() }
}
}
}

View file

@ -38,7 +38,9 @@ internal class DefaultWalletManagersRepository(
return findOrMakeInternal(userWallet, blockchainNetwork)
}
override suspend fun findOrMakeSingleCurrencyWalletManager(userWallet: UserWallet): CompletionResult<WalletManager> {
override suspend fun findOrMakeSingleCurrencyWalletManager(
userWallet: UserWallet,
): CompletionResult<WalletManager> {
return findOrMakeInternal(userWallet, blockchainNetwork = null)
}
@ -180,8 +182,11 @@ internal class DefaultWalletManagersRepository(
blockchain: Blockchain?,
): WalletManager? {
return walletManagersStorage.getAll().first()[userWalletId]?.let { userWalletManagers ->
if (blockchain == null) userWalletManagers.firstOrNull()
else userWalletManagers.find { it.wallet.blockchain == blockchain }
if (blockchain == null) {
userWalletManagers.firstOrNull()
} else {
userWalletManagers.find { it.wallet.blockchain == blockchain }
}
}
}

View file

@ -11,8 +11,8 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
internal object WalletManagerStorage {
private val managers =
MutableSharedFlow<HashMap<UserWalletId, List<WalletManager>>>(replay = 1)
private val managers = MutableSharedFlow<HashMap<UserWalletId, List<WalletManager>>>(replay = 1)
private val mutex = Mutex()
init {
managers.tryEmit(hashMapOf())
@ -22,7 +22,6 @@ internal object WalletManagerStorage {
return managers.asSharedFlow()
}
private val mutex = Mutex()
suspend fun update(
f: suspend (HashMap<UserWalletId, List<WalletManager>>) -> HashMap<UserWalletId, List<WalletManager>>,
) {

View file

@ -11,8 +11,8 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
internal object WalletStoresStorage {
private val stores =
MutableSharedFlow<HashMap<UserWalletId, List<WalletStoreModel>>>(replay = 1)
private val stores = MutableSharedFlow<HashMap<UserWalletId, List<WalletStoreModel>>>(replay = 1)
private val mutex = Mutex()
init {
stores.tryEmit(hashMapOf())
@ -22,7 +22,6 @@ internal object WalletStoresStorage {
return stores.asSharedFlow()
}
private val mutex = Mutex()
suspend fun update(
f: suspend (HashMap<UserWalletId, List<WalletStoreModel>>) -> HashMap<UserWalletId, List<WalletStoreModel>>,
) {

View file

@ -12,69 +12,65 @@ import com.trustwallet.walletconnect.models.binance.WCBinanceTransferOrder
import com.trustwallet.walletconnect.models.binance.tradeOrderSerializer
import timber.log.Timber
class BnbHelper {
companion object {
object BnbHelper {
fun createMessageData(order: WCBinanceTransferOrder): BinanceMessageData.Transfer {
val input = order.msgs.first().inputs.first()
val output = order.msgs.first().inputs.first()
fun createMessageData(order: WCBinanceTransferOrder): BinanceMessageData.Transfer {
val input = order.msgs.first().inputs.first()
val output = order.msgs.first().inputs.first()
val currency =
input.coins.firstOrNull()?.denom ?: Blockchain.Binance
val amount = input.coins
.mapNotNull { if (it.denom == currency) it.amount else null }
.sum()
.toBigDecimal()
.movePointLeft(Blockchain.Binance.decimals())
.stripZeroPlainString()
val currency =
input.coins.firstOrNull()?.denom ?: Blockchain.Binance
val amount = input.coins
.mapNotNull { if (it.denom == currency) it.amount else null }
.sum()
.toBigDecimal()
.movePointLeft(Blockchain.Binance.decimals())
.stripZeroPlainString()
val gson = GsonBuilder()
.registerTypeAdapter(tradeOrderSerializer)
.serializeNulls()
.create()
return BinanceMessageData.Transfer(
outputAddress = output.address,
amount = amount,
address = input.address,
data = gson.toJson(order).toByteArray().calculateSha256()
)
}
fun createMessageData(order: WCBinanceTradeOrder): BinanceMessageData.Trade {
val address = order.msgs.first().sender
val tradeData = order.msgs.map {
val price = it.price.toBigDecimal()
.movePointLeft(Blockchain.Binance.decimals())
.stripTrailingZeros()
val quantity = it.quantity.toBigDecimal()
.movePointLeft(Blockchain.Binance.decimals())
.stripTrailingZeros()
val amount = price * quantity
val symbol = it.symbol.substringBefore("-")
TradeData(
price = "$price ${Blockchain.Binance.currency}",
quantity = "$quantity $symbol",
amount = "$amount ${Blockchain.Binance.currency}",
symbol = symbol
)
}
val gson = GsonBuilder()
.registerTypeAdapter(tradeOrderSerializer)
.serializeNulls()
.create()
val serialized = gson.toJson(order)
Timber.d(serialized)
return BinanceMessageData.Trade(
tradeData = tradeData,
address = address,
data = serialized.toByteArray().calculateSha256()
)
}
val gson = GsonBuilder()
.registerTypeAdapter(tradeOrderSerializer)
.serializeNulls()
.create()
return BinanceMessageData.Transfer(
outputAddress = output.address,
amount = amount,
address = input.address,
data = gson.toJson(order).toByteArray().calculateSha256(),
)
}
}
fun createMessageData(order: WCBinanceTradeOrder): BinanceMessageData.Trade {
val address = order.msgs.first().sender
val tradeData = order.msgs.map {
val price = it.price.toBigDecimal()
.movePointLeft(Blockchain.Binance.decimals())
.stripTrailingZeros()
val quantity = it.quantity.toBigDecimal()
.movePointLeft(Blockchain.Binance.decimals())
.stripTrailingZeros()
val amount = price * quantity
val symbol = it.symbol.substringBefore("-")
TradeData(
price = "$price ${Blockchain.Binance.currency}",
quantity = "$quantity $symbol",
amount = "$amount ${Blockchain.Binance.currency}",
symbol = symbol,
)
}
val gson = GsonBuilder()
.registerTypeAdapter(tradeOrderSerializer)
.serializeNulls()
.create()
val serialized = gson.toJson(order)
Timber.d(serialized)
return BinanceMessageData.Trade(
tradeData = tradeData,
address = address,
data = serialized.toByteArray().calculateSha256(),
)
}
}

View file

@ -9,50 +9,28 @@ import com.google.gson.JsonParser
import com.google.gson.annotations.SerializedName
import com.tangem.blockchain.common.Blockchain
import com.trustwallet.walletconnect.JSONRPC_VERSION
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
class EthSignHelper {
companion object {
private val gson: Gson by lazy {
GsonBuilder()
.setPrettyPrinting()
.serializeNulls()
.create()
}
object EthSignHelper {
private val gson: Gson by lazy {
GsonBuilder()
.setPrettyPrinting()
.serializeNulls()
.create()
}
fun parseCustomRequest(data: String): CustomJsonRpcRequest {
return gson.fromJson<CustomJsonRpcRequest>(data)
}
fun tryToParseEthTypedMessage(request: CustomJsonRpcRequest): WCEthereumSignMessage? {
return if (request.method == WCMethodExtended.ETH_SIGN_TYPE_DATA_V4) {
WCEthereumSignMessage(
listOf(
request.params[0].asString,
request.params[1].asString,
),
WCEthereumSignMessage.WCSignType.TYPED_MESSAGE
)
} else {
null
}
}
fun tryToParseEthTypedMessageString(message: String): String? {
return try {
val messageString = message
.replace("\\", "")
.removePrefix("\"")
.removeSuffix("\"")
val messageJson = JsonParser().parse(messageString)
val filteredMap = gson.fromJson<Map<*,*>>(messageJson)
.filterKeys { it == "domain" || it == "message"}
gson.toJson(filteredMap)
} catch (exception: Exception) {
null
}
fun tryToParseEthTypedMessageString(message: String): String? {
return try {
val messageString = message
.replace("\\", "")
.removePrefix("\"")
.removeSuffix("\"")
val messageJson = JsonParser().parse(messageString)
val filteredMap = gson.fromJson<Map<*, *>>(messageJson)
.filterKeys { it == "domain" || it == "message" }
gson.toJson(filteredMap)
} catch (exception: Exception) {
null
}
}
}
@ -61,7 +39,7 @@ data class CustomJsonRpcRequest(
val id: Long,
val jsonrpc: String = JSONRPC_VERSION,
val method: WCMethodExtended?,
val params: JsonArray
val params: JsonArray,
) {
fun blockchainFromChainId(): Blockchain? {

View file

@ -3,8 +3,8 @@ package com.tangem.tap.domain.walletconnect
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.guard
import com.tangem.domain.common.ScanResponse
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.global.GlobalAction
@ -43,6 +43,7 @@ import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.collections.set
@Suppress("LargeClass")
class WalletConnectManager {
private var cardId: String? = null
@ -126,6 +127,7 @@ class WalletConnectManager {
}
}
@Suppress("MagicNumber")
private fun setupConnectionTimeoutCheck(session: WCSession) {
scope.launch {
delay(20_000)
@ -345,6 +347,7 @@ class WalletConnectManager {
}
}
@Suppress("LongMethod", "ComplexMethod")
fun setListeners(client: WCClient) {
client.onSessionRequest = { id: Long, peer: WCPeerMeta ->
Timber.d("OnSessionRequest: $peer")
@ -490,12 +493,11 @@ class WalletConnectManager {
}
companion object {
private val tangemPeerMeta =
WCPeerMeta(name = "Tangem Wallet", url = "https://tangem.com")
const val WC_SCHEME = "wc"
private val tangemPeerMeta = WCPeerMeta(name = "Tangem Wallet", url = "https://tangem.com")
fun isCorrectWcUri(string: String): Boolean = WCSession.from(string) != null
const val WC_SCHEME = "wc"
}
}
@ -521,6 +523,7 @@ data class WalletConnectActiveData(
}
}
@Suppress("MagicNumber")
class RetryInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()

View file

@ -3,46 +3,39 @@ package com.tangem.tap.domain.walletconnect
import com.tangem.blockchain.common.Blockchain
import com.trustwallet.walletconnect.models.WCPeerMeta
class WalletConnectNetworkUtils {
companion object {
fun parseBlockchain(
chainId: Int?,
peer: WCPeerMeta,
): Blockchain? {
return when {
peer.url.contains("pancakeswap.finance") -> {
Blockchain.BSC
}
peer.url.contains("optimism") -> {
Blockchain.Optimism
}
chainId != null -> {
Blockchain.fromChainId(chainId)
}
peer.url.contains("matic.network") || peer.name == "Polygon" -> {
Blockchain.Polygon
}
peer.url.contains("binance.org") || peer.name.contains("Binance") -> {
if (peer.icons.firstOrNull()?.contains("testnet") == true) {
Blockchain.BinanceTestnet
} else {
Blockchain.Binance
}
}
peer.name.contains("BSC") -> {
Blockchain.BSC
}
peer.url.contains("honeyswap.1hive.eth.limo") -> {
// Check if something's changed after this bug report:
// https://github.com/1Hive/honeyswap-interface/issues/83
Blockchain.Gnosis
}
else -> {
Blockchain.Ethereum
object WalletConnectNetworkUtils {
fun parseBlockchain(chainId: Int?, peer: WCPeerMeta): Blockchain? {
return when {
peer.url.contains("pancakeswap.finance") -> {
Blockchain.BSC
}
peer.url.contains("optimism") -> {
Blockchain.Optimism
}
chainId != null -> {
Blockchain.fromChainId(chainId)
}
peer.url.contains("matic.network") || peer.name == "Polygon" -> {
Blockchain.Polygon
}
peer.url.contains("binance.org") || peer.name.contains("Binance") -> {
if (peer.icons.firstOrNull()?.contains("testnet") == true) {
Blockchain.BinanceTestnet
} else {
Blockchain.Binance
}
}
peer.name.contains("BSC") -> {
Blockchain.BSC
}
peer.url.contains("honeyswap.1hive.eth.limo") -> {
// Check if something's changed after this bug report:
// https://github.com/1Hive/honeyswap-interface/issues/83
Blockchain.Gnosis
}
else -> {
Blockchain.Ethereum
}
}
}
}

View file

@ -22,9 +22,9 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toDecompressedPublicKey
import com.tangem.common.extensions.toHexString
import com.tangem.core.analytics.Analytics
import com.tangem.crypto.CryptoUtils
import com.tangem.operations.sign.SignHashCommand
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.extensions.toFormattedString
@ -48,6 +48,7 @@ import java.math.BigDecimal
class WalletConnectSdkHelper {
@Suppress("MagicNumber")
suspend fun prepareTransactionData(
transaction: WCEthereumTransaction,
session: WalletConnectSession,
@ -101,7 +102,7 @@ class WalletConnectSdkHelper {
gasAmount = fee.toFormattedString(decimals),
totalAmount = total.toFormattedString(decimals),
balance = balance.toFormattedString(decimals),
isEnoughFundsToSend = (balance - total) >= BigDecimal.ZERO,
isEnoughFundsToSend = balance - total >= BigDecimal.ZERO,
session = session.session,
id = id,
type = type,

View file

@ -8,9 +8,10 @@ import org.rekotlin.StoreSubscriber
*/
abstract class BaseStoreFragment(layoutId: Int) : BaseFragment(layoutId) {
abstract fun subscribeToStore()
protected val storeSubscribersList = mutableListOf<StoreSubscriber<*>>()
abstract fun subscribeToStore()
override fun onStart() {
super.onStart()
subscribeToStore()

View file

@ -82,6 +82,7 @@ object DemoHelper {
}
}
@Suppress("LargeClass")
class DemoConfig {
val demoBlockchains = listOf(
@ -114,6 +115,7 @@ class DemoConfig {
return (releaseDemoCardIds + testDemoCardIds).distinct()
}
@Suppress("ClassOrdering")
private val releaseDemoCardIds = mutableListOf(
// === Not from the Google Sheet table ===
"FB10000000000196", // Note BTC
@ -476,20 +478,19 @@ class DemoConfig {
"AB02000000058187",
)
@Suppress("ClassOrdering")
private val testDemoCardIds = listOf(
"FB20000000000186", // Note ETH
"FB10000000000196", // Note BTC
"FB30000000000176", // Wallet
)
@Suppress("ClassOrdering")
private val debugTestDemoCardIds = listOf<String>(
)
}
class DemoTransactionSender(
private val walletManager: WalletManager,
private val sender: TransactionSender = walletManager as TransactionSender
) : TransactionSender {
class DemoTransactionSender(private val walletManager: WalletManager) : TransactionSender {
override suspend fun getFee(amount: Amount, destination: String): Result<List<Amount>> {
val blockchain = walletManager.wallet.blockchain
@ -502,6 +503,7 @@ class DemoTransactionSender(
)
}
@Suppress("MagicNumber")
override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult {
val dataToSign = randomString(32).toByteArray()
val signerResponse = signer.sign(

View file

@ -39,7 +39,7 @@ internal class DemoOnboardingNoteMiddleware : DemoMiddleware {
currency = Currency.Blockchain(walletManager.wallet.blockchain, null),
state = ProgressState.Done,
error = null,
criticalError = null
criticalError = null,
)
walletManager.wallet.setAmount(balanceAmount)

View file

@ -5,8 +5,8 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.flatMap
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.extensions.dispatchDialogShow
@ -146,6 +146,7 @@ class DetailsMiddleware {
}
class ManageSecurityMiddleware {
@Suppress("ComplexMethod")
fun handle(action: DetailsAction.ManageSecurity) {
when (action) {
is DetailsAction.ManageSecurity.OpenSecurity -> {

View file

@ -15,10 +15,8 @@ import com.tangem.tap.tangemSdkManager
import org.rekotlin.Action
import java.util.*
class DetailsReducer {
companion object {
fun reduce(action: Action, state: AppState): DetailsState = internalReduce(action, state)
}
object DetailsReducer {
fun reduce(action: Action, state: AppState): DetailsState = internalReduce(action, state)
}
private fun internalReduce(action: Action, state: AppState): DetailsState {
@ -104,8 +102,7 @@ private fun prepareSecurityOptions(card: CardDTO): ManageSecurityState {
private fun isResetToFactoryAllowedByCard(card: CardDTO): Boolean {
val notAllowedByAnyWallet = card.wallets.any { it.settings.isPermanent }
val notAllowedByCard = notAllowedByAnyWallet ||
(card.isWalletDataSupported && (!card.isTangemNote && !card.settings.isBackupAllowed)) ||
card.isSaltPay
card.isWalletDataSupported && !card.isTangemNote && !card.settings.isBackupAllowed || card.isSaltPay
return !notAllowedByCard
}
@ -188,6 +185,7 @@ private fun prepareAllowedSecurityOptions(
return allowedSecurityOptions
}
@Suppress("MagicNumber")
private fun CardDTO.toCardInfo(): CardInfo {
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
val issuer = this.issuer.name

View file

@ -40,6 +40,7 @@ class WalletConnectMiddleware {
}
}
@Suppress("ComplexMethod", "LongMethod")
private fun handle(state: () -> AppState?, action: Action) {
if (DemoHelper.tryHandle(state, action)) return
@ -49,10 +50,8 @@ class WalletConnectMiddleware {
walletConnectManager.restoreSessions(action.scanResponse)
}
is WalletConnectAction.HandleDeepLink -> {
if (!action.wcUri.isNullOrBlank()) {
if (WalletConnectManager.isCorrectWcUri(action.wcUri)) {
store.dispatchOnMain(WalletConnectAction.OpenSession(action.wcUri))
}
if (!action.wcUri.isNullOrBlank() && WalletConnectManager.isCorrectWcUri(action.wcUri)) {
store.dispatchOnMain(WalletConnectAction.OpenSession(action.wcUri))
}
}
is WalletConnectAction.StartWalletConnect -> {

View file

@ -2,47 +2,44 @@ package com.tangem.tap.features.details.redux.walletconnect
import org.rekotlin.Action
class WalletConnectReducer {
companion object {
fun reduce(
action: Action, state: WalletConnectState,
): WalletConnectState {
if (action !is WalletConnectAction) return state
object WalletConnectReducer {
fun reduce(
action: Action, state: WalletConnectState,
): WalletConnectState {
if (action !is WalletConnectAction) return state
return when (action) {
is WalletConnectAction.ResetState -> return WalletConnectState()
is WalletConnectAction.ApproveSession.Success -> {
state.copy(
loading = false,
sessions = state.sessions + action.session,
)
}
is WalletConnectAction.OpenSession -> {
state.copy(loading = true)
}
is WalletConnectAction.SetNewSessionData -> {
state.copy(newSessionData = action.newSession)
}
is WalletConnectAction.SetSessionsRestored ->
WalletConnectState(sessions = action.sessions)
is WalletConnectAction.RemoveSession -> {
val sessions =
state.sessions.filterNot { it.session.toUri() == action.session.toUri() }
state.copy(sessions = sessions)
}
is WalletConnectAction.UnsupportedCard -> state.copy(loading = false)
is WalletConnectAction.RefuseOpeningSession -> state.copy(loading = false)
is WalletConnectAction.OpeningSessionTimeout -> state.copy(loading = false)
is WalletConnectAction.FailureEstablishingSession -> state.copy(loading = false)
is WalletConnectAction.UpdateBlockchain -> state.copy(
sessions = state.sessions
.filterNot { it.peerId == action.updatedSession.peerId }
+ action.updatedSession
return when (action) {
is WalletConnectAction.ResetState -> return WalletConnectState()
is WalletConnectAction.ApproveSession.Success -> {
state.copy(
loading = false,
sessions = state.sessions + action.session,
)
else -> state
}
}
is WalletConnectAction.OpenSession -> {
state.copy(loading = true)
}
is WalletConnectAction.SetNewSessionData -> {
state.copy(newSessionData = action.newSession)
}
is WalletConnectAction.SetSessionsRestored ->
WalletConnectState(sessions = action.sessions)
is WalletConnectAction.RemoveSession -> {
val sessions =
state.sessions.filterNot { it.session.toUri() == action.session.toUri() }
state.copy(sessions = sessions)
}
is WalletConnectAction.UnsupportedCard -> state.copy(loading = false)
is WalletConnectAction.RefuseOpeningSession -> state.copy(loading = false)
is WalletConnectAction.OpeningSessionTimeout -> state.copy(loading = false)
is WalletConnectAction.FailureEstablishingSession -> state.copy(loading = false)
is WalletConnectAction.UpdateBlockchain -> state.copy(
sessions = state.sessions
.filterNot { it.peerId == action.updatedSession.peerId }
+ action.updatedSession,
)
else -> state
}
}
}

View file

@ -74,7 +74,7 @@ data class WalletForSession(
}
override fun hashCode(): Int {
var result = (walletPublicKey?.contentHashCode() ?: 0)
var result = walletPublicKey?.contentHashCode() ?: 0
result = 31 * result + (derivedPublicKey?.contentHashCode() ?: 0)
result = 31 * result + (derivationPath?.hashCode() ?: 0)
result = 31 * result + isTestNet.hashCode()

View file

@ -89,6 +89,7 @@ private fun AppSettings(
}
}
@Suppress("LongMethod")
@Composable
private fun AppSettingsElement(
modifier: Modifier = Modifier,

View file

@ -51,6 +51,7 @@ fun CardSettingsScreen(
)
}
@Suppress("MagicNumber")
@Composable
fun CardSettingsReadCard(
onScanCardClick: () -> Unit,
@ -114,6 +115,7 @@ fun CardSettingsReadCard(
}
}
@Suppress("ComplexMethod")
@Composable
fun CardSettings(
state: CardSettingsScreenState,

View file

@ -1,14 +1,13 @@
package com.tangem.tap.features.details.ui.cardsettings
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.getTwinCardIdForUser
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.details.redux.CardSettingsState
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.wallet.R
import org.rekotlin.Store
class CardSettingsViewModel(private val store: Store<AppState>) {

Some files were not shown because too many files have changed in this diff Show more