diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt index bad36d3f0d..e46716ae0c 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt @@ -60,8 +60,18 @@ sealed class AnalyticsParam { object BlockchainSdk : Error("Blockchain Sdk Error") } + sealed class ScannedFrom(val value: String) { + object Introduction : ScannedFrom("Introduction") + object Main : ScannedFrom("Main") + object SignIn : ScannedFrom("Sign In") + object MyWallets : ScannedFrom("My Wallets") + } + companion object Key { - const val BatchId = "Batch" + const val Batch = "Batch" + const val ProductType = "Product Type" + const val Firmware = "Firmware" + const val Currency = "Currency" const val ErrorDescription = "Error Description" const val ErrorCode = "Error Code" const val ErrorKey = "Error Key" diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt index ecdedd9761..7a7e9f245e 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt @@ -11,6 +11,15 @@ sealed class Basic( error: Throwable? = null, ) : AnalyticsEvent("Basic", event, params, error) { + class CardWasScanned( + source: AnalyticsParam.ScannedFrom, + ) : Basic( + event = "Card Was Scanned", + params = mapOf( + "Source" to source.value, + ), + ) + class SignedIn( state: AnalyticsParam.CardBalanceState, currency: AnalyticsParam.CardCurrency, @@ -19,14 +28,14 @@ sealed class Basic( event = "Signed in", params = mapOf( "State" to state.value, - "Currency" to currency.value, - AnalyticsParam.BatchId to batch, + AnalyticsParam.Currency to currency.value, + AnalyticsParam.Batch to batch, ), ) class ToppedUp(currency: AnalyticsParam.CardCurrency) : Basic( event = "Topped up", - params = mapOf("Currency" to currency.value), + params = mapOf(AnalyticsParam.Currency to currency.value), ) class ScanError(error: Throwable) : Basic( diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/IntroductionProcess.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/IntroductionProcess.kt index 0d799d379f..602365c0c6 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/IntroductionProcess.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/IntroductionProcess.kt @@ -14,6 +14,5 @@ sealed class IntroductionProcess( class ButtonTokensList : IntroductionProcess("Button - Tokens List") class ButtonBuyCards : IntroductionProcess("Button - Buy Cards") class ButtonScanCard : IntroductionProcess("Button - Scan Card") - class CardWasScanned : IntroductionProcess("Card Was Scanned") class ButtonRequestSupport : IntroductionProcess("Button - Request Support") } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt index 6f718755f0..9ce35483a4 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt @@ -13,7 +13,6 @@ sealed class MainScreen( class ScreenOpened : MainScreen("Screen opened") class ButtonScanCard : MainScreen("Button - Scan Card") - class CardWasScanned : MainScreen("Card Was Scanned") class ButtonMyWallets : MainScreen("Button - My Wallets") class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen( diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt index 4db0526ad2..65ab7937c2 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt @@ -8,7 +8,6 @@ sealed class MyWallets( ) : AnalyticsEvent("My Wallets", event, params) { class MyWalletsScreenOpened : MyWallets(event = "My Wallets Screen Opened") - class CardWasScanned : MyWallets(event = "Card Was Scanned") sealed class Button { class ScanNewCard : MyWallets(event = "Button - Scan New Card") diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt index 8eb0f6490d..0b203bab27 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt @@ -51,7 +51,7 @@ sealed class Onboarding( class ButtonBuyCrypto(currency: AnalyticsParam.CurrencyType) : Topup( event = "Button - Buy Crypto", - params = mapOf("Currency" to currency.value), + params = mapOf(AnalyticsParam.Currency to currency.value), ) class ButtonShowWalletAddress : Topup("Button - Show the Wallet Address") @@ -67,8 +67,10 @@ sealed class Onboarding( class SetupFinished : Twins("Twin Setup Finished") } + class PinScreenOpened : Onboarding("Onboarding", "PIN screen opened") class PinCodeSet : Onboarding("Onboarding", "PIN code set") class ButtonConnect : Onboarding("Onboarding", "Button - Connect") + class KYCScreenOpened : Onboarding("Onboarding", "KYC screen opened") class KYCStarted : Onboarding("Onboarding", "KYC started") class KYCInProgress : Onboarding("Onboarding", "KYC in progress") class KYCRejected : Onboarding("Onboarding", "KYC rejected") diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt index cad34430d9..a5417d775b 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/SignIn.kt @@ -12,7 +12,6 @@ sealed class SignIn( ) : AnalyticsEvent("Sign In", event, params, error) { class ScreenOpened : SignIn(event = "Sing In Screen Opened") - class CardWasScanned : SignIn(event = "Card Was Scanned") class ButtonBiometricSignIn : SignIn(event = "Button - Biometric Sign In") class ButtonCardSignIn : SignIn(event = "Button - Card Sign In") diff --git a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/BatchIdParamsInterceptor.kt b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/BatchIdParamsInterceptor.kt deleted file mode 100644 index b7e7ec6fbf..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/BatchIdParamsInterceptor.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.common.analytics.paramsInterceptor - -import com.tangem.core.analytics.api.ParamsInterceptor -import com.tangem.core.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.events.AnalyticsParam - -/** -[REDACTED_AUTHOR] - */ -class BatchIdParamsInterceptor( - val batchId: String, -) : ParamsInterceptor { - - override fun id(): String = this::class.java.simpleName - - override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = true - - override fun intercept(params: MutableMap) { - params[AnalyticsParam.BatchId] = batchId - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt new file mode 100644 index 0000000000..99c231e1e4 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt @@ -0,0 +1,65 @@ +package com.tangem.tap.common.analytics.paramsInterceptor + +import com.tangem.core.analytics.AnalyticsEvent +import com.tangem.core.analytics.api.ParamsInterceptor +import com.tangem.domain.common.ProductType +import com.tangem.domain.common.ScanResponse +import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter +import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.features.demo.DemoHelper + +/** +[REDACTED_AUTHOR] + */ +class CardContextInterceptor( + private val scanResponse: ScanResponse?, +) : ParamsInterceptor { + + override fun id(): String = CardContextInterceptor.id() + + override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = true + + override fun intercept(params: MutableMap) { + scanResponse ?: return + + val card = scanResponse.card + params[AnalyticsParam.Batch] = card.batchId + params[AnalyticsParam.ProductType] = getProductType(scanResponse) + params[AnalyticsParam.Firmware] = card.firmwareVersion.stringValue + + ParamCardCurrencyConverter().convert(scanResponse)?.let { + params[AnalyticsParam.Currency] = it.value + } + } + + private fun getProductType(scanResponse: ScanResponse): String { + return when (scanResponse.productType) { + ProductType.Note -> "Note" + ProductType.Twins -> "Twin" + ProductType.Wallet -> "Wallet" + ProductType.SaltPay -> if (scanResponse.isSaltPayVisa()) { + "Visa" + } else { + "Visa Backup" + } + ProductType.Start2Coin -> "Start2Coin" + else -> if (DemoHelper.isDemoCard(scanResponse)) { + if (DemoHelper.isTestDemoCard(scanResponse)) { + "Demo Test" + } else { + when (scanResponse.card.cardId.substring(0..1)) { + "AC" -> "Demo Wallet" + "AB" -> "Demo Note" + else -> "Demo Other" + } + } + } else { + "Other" + } + } + } + + companion object { + fun id(): String = CardContextInterceptor::class.java.simpleName + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Analytics.kt b/app/src/main/java/com/tangem/tap/common/extensions/Analytics.kt new file mode 100644 index 0000000000..3772f86a71 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Analytics.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.common.extensions + +import com.tangem.core.analytics.Analytics +import com.tangem.domain.common.ScanResponse +import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor + +/** +[REDACTED_AUTHOR] + */ +fun Analytics.addCardContext(scanResponse: ScanResponse) { + addParamsInterceptor(CardContextInterceptor(scanResponse)) +} + +fun Analytics.eraseCardContext() { + removeParamsInterceptor(CardContextInterceptor.id()) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/ScanCardProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/ScanCardProcessor.kt index acf77e3bdb..21c1cd221a 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/ScanCardProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/ScanCardProcessor.kt @@ -13,7 +13,7 @@ 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.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor +import com.tangem.tap.common.extensions.addCardContext import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.primaryCardIsSaltPayVisa import com.tangem.tap.common.redux.global.GlobalAction @@ -75,7 +75,7 @@ object ScanCardProcessor { tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse) onScanStateChange(false) - sendAnalytics(analyticsEvent, scanResponse.card.batchId) + sendAnalytics(analyticsEvent, scanResponse) checkForUnfinishedBackupForSaltPay( backupService = backupService, @@ -103,9 +103,9 @@ object ScanCardProcessor { private fun sendAnalytics( analyticsEvent: AnalyticsEvent?, - batchId: String, + scanResponse: ScanResponse, ) { - Analytics.addParamsInterceptor(BatchIdParamsInterceptor(batchId)) + Analytics.addCardContext(scanResponse) analyticsEvent?.let { Analytics.send(it) } } diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt index b0d473cde0..d984e73f13 100644 --- a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt @@ -49,6 +49,8 @@ object DemoHelper { fun isDemoCard(scanResponse: ScanResponse): Boolean = isDemoCardId(scanResponse.card.cardId) + fun isTestDemoCard(scanResponse: ScanResponse): Boolean = config.isTestDemoCardId(scanResponse.card.cardId) + fun isDemoCardId(cardId: String): Boolean = config.isDemoCardId(cardId) fun tryHandle(appState: () -> AppState?, action: Action): Boolean { @@ -108,6 +110,8 @@ class DemoConfig { fun isDemoCardId(cardId: String): Boolean = demoCardIds.contains(cardId) + fun isTestDemoCardId(cardId: String): Boolean = testDemoCardIds.contains(cardId) + fun getBalance(blockchain: Blockchain): Amount = walletBalances[blockchain]?.copy() ?: Amount(BigDecimal.ZERO, blockchain).copy() @@ -117,9 +121,6 @@ class DemoConfig { private val releaseDemoCardIds = mutableListOf( // === Not from the Google Sheet table === - "FB10000000000196", // Note BTC - "FB20000000000186", // Note ETH - "FB30000000000176", // Wallet "AC01000000041225", "AC01000000041472", "AB01000000046498", @@ -489,17 +490,19 @@ class DemoConfig { class DemoTransactionSender( private val walletManager: WalletManager, - private val sender: TransactionSender = walletManager as TransactionSender + private val sender: TransactionSender = walletManager as TransactionSender, ) : TransactionSender { override suspend fun getFee(amount: Amount, destination: String): Result> { val blockchain = walletManager.wallet.blockchain return when (walletManager) { - is BitcoinWalletManager -> Result.Success(listOf( - Amount(0.0001.toBigDecimal(), blockchain), - Amount(0.0003.toBigDecimal(), blockchain), - Amount(0.00055.toBigDecimal(), blockchain), - )) + is BitcoinWalletManager -> Result.Success( + listOf( + Amount(0.0001.toBigDecimal(), blockchain), + Amount(0.0003.toBigDecimal(), blockchain), + Amount(0.00055.toBigDecimal(), blockchain), + ), + ) else -> sender.getFee(amount, destination) } } @@ -508,7 +511,7 @@ class DemoTransactionSender( val dataToSign = randomString(32).toByteArray() val signerResponse = signer.sign( hash = dataToSign, - publicKey = walletManager.wallet.publicKey + publicKey = walletManager.wallet.publicKey, ) return when (signerResponse) { is CompletionResult.Success -> SimpleResult.Failure(Exception(ID).toBlockchainSdkError()) @@ -529,5 +532,4 @@ class DemoTransactionSender( companion object { val ID = DemoTransactionSender::class.java.simpleName } - } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 60dc234e4f..3395d8b1f0 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -29,7 +29,7 @@ class HomeFragment : Fragment(), StoreSubscriber { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Analytics.send(IntroductionProcess.ScreenOpened()) + store.dispatch(HomeAction.OnCreate) store.dispatch(HomeAction.Init) } diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt index 59e169ee04..bb9be2edc3 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt @@ -1,16 +1,18 @@ package com.tangem.tap.features.home.redux import com.tangem.core.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.events.IntroductionProcess +import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.entities.IndeterminateProgressButton import org.rekotlin.Action sealed class HomeAction : Action { + object OnCreate : HomeAction() object Init : HomeAction() data class ReadCard( - val analyticsEvent: AnalyticsEvent? = IntroductionProcess.CardWasScanned(), + val analyticsEvent: AnalyticsEvent? = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Introduction), ) : HomeAction() data class ScanInProgress(val scanInProgress: Boolean) : HomeAction() diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 76048ed0f4..afcb5c7a14 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -5,10 +5,12 @@ import com.tangem.common.doOnResult import com.tangem.common.doOnSuccess import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.AnalyticsEvent +import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.analytics.events.Shop import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchOpenUrl +import com.tangem.tap.common.extensions.eraseCardContext import com.tangem.tap.common.extensions.onCardScanned import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.redux.AppState @@ -51,6 +53,10 @@ private val homeMiddleware: Middleware = { _, _ -> private fun handleHomeAction(action: Action) { when (action) { + is HomeAction.OnCreate -> { + Analytics.eraseCardContext() + Analytics.send(IntroductionProcess.ScreenOpened()) + } is HomeAction.Init -> { store.dispatch(GlobalAction.RestoreAppCurrency) store.dispatch(GlobalAction.ExchangeManager.Init) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt index 5f39361096..ad903e6dd1 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt @@ -258,6 +258,8 @@ private fun handleOnboardingSaltPayAction(anyAction: Action, appState: () -> App } is OnboardingSaltPayAction.SetStep -> { when (action.newStep) { + SaltPayActivationStep.NeedPin -> Analytics.send(Onboarding.PinScreenOpened()) + SaltPayActivationStep.KycIntro -> Analytics.send(Onboarding.KYCScreenOpened()) SaltPayActivationStep.KycStart -> Analytics.send(Onboarding.KYCStarted()) SaltPayActivationStep.KycWaiting -> Analytics.send(Onboarding.KYCInProgress()) SaltPayActivationStep.KycReject -> Analytics.send(Onboarding.KYCRejected()) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index f75992fe2b..22aba65cfc 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -15,6 +15,7 @@ import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.common.analytics.converters.BasicEventsPreChecker import com.tangem.tap.common.analytics.converters.BasicEventsSourceData import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.analytics.events.Token import com.tangem.tap.common.extensions.copyToClipboard @@ -378,7 +379,7 @@ class WalletMiddleware { } else -> { Analytics.send(MainScreen.ButtonScanCard()) - store.dispatch(WalletAction.Scan(MainScreen.CardWasScanned())) + store.dispatch(WalletAction.Scan(Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main))) } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/HistoryTransactionData.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/HistoryTransactionData.kt new file mode 100644 index 0000000000..50b80f0b37 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/HistoryTransactionData.kt @@ -0,0 +1,21 @@ +package com.tangem.tap.features.wallet.ui.wallet.saltPay + +import com.tangem.blockchain.common.TransactionData +import com.tangem.blockchain.common.TransactionStatus +import com.tangem.tap.features.wallet.models.PendingTransactionType + +/** +[REDACTED_AUTHOR] + */ +data class HistoryTransactionData( + val transactionData: TransactionData, + private val walletAddress: String, +) { + fun isInProgress(): Boolean = transactionData.status == TransactionStatus.Unconfirmed + + fun getTransactionType(): PendingTransactionType = when { + transactionData.sourceAddress.lowercase() == walletAddress.lowercase() -> PendingTransactionType.Outgoing + transactionData.destinationAddress.lowercase() == walletAddress.lowercase() -> PendingTransactionType.Incoming + else -> PendingTransactionType.Unknown + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/rv/ShimmerRecyclerAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/rv/ShimmerRecyclerAdapter.kt new file mode 100644 index 0000000000..fc59d49155 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/rv/ShimmerRecyclerAdapter.kt @@ -0,0 +1,33 @@ +package com.tangem.tap.features.wallet.ui.wallet.saltPay.rv + +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView + +/** +[REDACTED_AUTHOR] + */ +open class ShimmerRecyclerAdapter( + private val viewHolderViewFactory: (ViewGroup) -> ViewGroup, +) : ListAdapter(DiffUtilCallback) { + + override fun getItemId(position: Int): Long { + return if (currentList.isEmpty()) 0 else currentList[position].hashCode().toLong() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShimmerVH { + return ShimmerVH(viewHolderViewFactory.invoke(parent)) + } + + override fun onBindViewHolder(holder: ShimmerVH, position: Int) {} + + object DiffUtilCallback : DiffUtil.ItemCallback() { + override fun areContentsTheSame(oldItem: ShimmerData, newItem: ShimmerData) = oldItem == newItem + override fun areItemsTheSame(oldItem: ShimmerData, newItem: ShimmerData) = oldItem == newItem + } +} + +class ShimmerVH(viewGroup: ViewGroup) : RecyclerView.ViewHolder(viewGroup) + +data class ShimmerData(private val any: String = "") \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/rv/TransactionHistoryAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/rv/TransactionHistoryAdapter.kt new file mode 100644 index 0000000000..bc5f923aff --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/saltPay/rv/TransactionHistoryAdapter.kt @@ -0,0 +1,103 @@ +package com.tangem.tap.features.wallet.ui.wallet.saltPay.rv + +import android.graphics.PorterDuff +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.tangem.tap.common.extensions.getColor +import com.tangem.tap.common.extensions.setDrawable +import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.extensions.toFormattedCurrencyString +import com.tangem.tap.features.wallet.models.PendingTransactionType +import com.tangem.tap.features.wallet.ui.wallet.saltPay.HistoryTransactionData +import com.tangem.wallet.R +import com.tangem.wallet.databinding.ItemSaltPayTxHistoryBinding + +/** +[REDACTED_AUTHOR] + */ +internal class TransactionHistoryAdapter : ListAdapter(DiffUtilCallback) { + + override fun getItemId(position: Int): Long { + return if (currentList.isEmpty()) 0 + else currentList[position].transactionData.hash.hashCode().toLong() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionItemVH { + val inflater = LayoutInflater.from(parent.context) + val layout = ItemSaltPayTxHistoryBinding.inflate(inflater, parent, false) + return TransactionItemVH(layout) + } + + override fun onBindViewHolder(holder: TransactionItemVH, position: Int) { + holder.bind(currentList[position]) + } + + object DiffUtilCallback : DiffUtil.ItemCallback() { + override fun areContentsTheSame( + oldItem: HistoryTransactionData, + newItem: HistoryTransactionData, + ) = oldItem == newItem + + override fun areItemsTheSame( + oldItem: HistoryTransactionData, + newItem: HistoryTransactionData, + ) = oldItem == newItem + } +} + +internal class TransactionItemVH( + private val binding: ItemSaltPayTxHistoryBinding, +) : RecyclerView.ViewHolder(binding.root) { + + fun bind(data: HistoryTransactionData) = with(binding) { + setupImage(data) + setupTitle(data) + setupSubtitle(data) + setupBalance(data) + } + + private fun setupImage(data: HistoryTransactionData) = with(binding) { + val drawable = when (data.getTransactionType()) { + PendingTransactionType.Incoming -> R.drawable.ic_tx_incoming + PendingTransactionType.Outgoing -> R.drawable.ic_tx_outgoing + PendingTransactionType.Unknown -> null + } + drawable?.let { imvTx.setDrawable(it) } + + if (data.isInProgress()) { + imvTx.setColorFilter(imvTx.getColor(R.color.icon_attention), PorterDuff.Mode.MULTIPLY) + } else { + imvTx.clearColorFilter() + } + } + + private fun setupTitle(data: HistoryTransactionData) = with(binding) { + val hash = data.transactionData.hash ?: return@with + + val formattedHash = "${hash.substring(0..5)}...${hash.substring(hash.length - 4)}" + tvTxHash.text = formattedHash + } + + private fun setupSubtitle(data: HistoryTransactionData) = with(binding) { + val inProgress = data.isInProgress() + tvTxStatus.show(inProgress) + tvTxTime.show(!inProgress) + + val time = data.transactionData.date?.timeInMillis.toString() ?: "none" + tvTxTime.text = time + } + + private fun setupBalance(data: HistoryTransactionData) = with(binding) { + val sign = when (data.getTransactionType()) { + PendingTransactionType.Incoming -> "+" + PendingTransactionType.Outgoing -> "-" + PendingTransactionType.Unknown -> "" + } + val amount = data.transactionData.amount + tvTxAmountSign.text = sign + tvTxAmountValue.text = amount.value?.toFormattedCurrencyString(8, amount.currencySymbol) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt index aa4f559888..1a98a9c93d 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt @@ -9,8 +9,10 @@ import com.tangem.common.map import com.tangem.core.analytics.Analytics import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.util.UserWalletId +import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.MyWallets -import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor +import com.tangem.tap.common.extensions.addCardContext import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.redux.AppState @@ -136,7 +138,7 @@ internal class WalletSelectorMiddleware { ) ScanCardProcessor.scan( - analyticsEvent = MyWallets.CardWasScanned(), + analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.MyWallets), onWalletNotCreated = { // No need to rollback policy, continue with the policy set before the card scan store.dispatchOnMain(WalletSelectorAction.AddWallet.Success) @@ -197,8 +199,7 @@ internal class WalletSelectorMiddleware { .doOnSuccess { val selectedUserWallet = userWalletsListManager.selectedUserWalletSync if (selectedUserWallet != null) { - val batchId = selectedUserWallet.scanResponse.card.batchId - Analytics.addParamsInterceptor(BatchIdParamsInterceptor(batchId)) + Analytics.addCardContext(selectedUserWallet.scanResponse) store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) store.onUserWalletSelected(selectedUserWallet) diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt index 681a3cea77..f33cfe9453 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt @@ -5,6 +5,7 @@ import com.tangem.common.core.TangemError import org.rekotlin.Action internal sealed interface WelcomeAction : Action { + object OnCreate: WelcomeAction object ProceedWithBiometrics : WelcomeAction { object Success : WelcomeAction data class Error(val error: TangemError) : WelcomeAction diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 4c39b5cb3c..76c0a2319e 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -4,9 +4,14 @@ import android.content.Intent import com.tangem.common.core.TangemSdkError import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess +import com.tangem.core.analytics.Analytics import com.tangem.domain.common.ScanResponse +import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.SignIn +import com.tangem.tap.common.extensions.addCardContext import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.eraseCardContext import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.navigation.AppScreen @@ -38,6 +43,10 @@ internal class WelcomeMiddleware { private fun handleAction(action: WelcomeAction, state: WelcomeState) { when (action) { + is WelcomeAction.OnCreate -> { + Analytics.eraseCardContext() + Analytics.send(SignIn.ScreenOpened()) + } is WelcomeAction.ProceedWithBiometrics -> { proceedWithBiometry(state) } @@ -62,6 +71,7 @@ internal class WelcomeMiddleware { } .doOnSuccess { selectedUserWallet -> if (selectedUserWallet != null) { + Analytics.addCardContext(selectedUserWallet.scanResponse) store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet)) store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics.Success) store.onUserWalletSelected(selectedUserWallet) @@ -101,7 +111,7 @@ internal class WelcomeMiddleware { useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes, ) ScanCardProcessor.scan( - analyticsEvent = SignIn.CardWasScanned(), + analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.SignIn), onSuccess = { scanResponse -> scope.launch { onCardScanned(scanResponse) } }, diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt index 42a6d4d0e1..e4abfb4208 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt @@ -26,6 +26,7 @@ internal object WelcomeReducer { is WelcomeAction.ProceedWithBiometrics.Success -> state.copy(isUnlockWithBiometricsInProgress = false) is WelcomeAction.ProceedWithCard.Success -> state.copy(isUnlockWithCardInProgress = false) is WelcomeAction.CloseError -> state.copy(error = null) + else -> state } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt index 70477f5ad1..9ca40680ac 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt @@ -19,13 +19,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.fragment.app.viewModels -import com.tangem.core.analytics.Analytics import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.fragments.ComposeFragment import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.common.analytics.events.SignIn import com.tangem.tap.features.details.ui.cardsettings.resolveReference +import com.tangem.tap.features.welcome.redux.WelcomeAction import com.tangem.tap.features.welcome.ui.components.WelcomeScreenContent +import com.tangem.tap.store import com.tangem.wallet.R internal class WelcomeFragment : ComposeFragment() { @@ -33,7 +33,7 @@ internal class WelcomeFragment : ComposeFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Analytics.send(SignIn.ScreenOpened()) + store.dispatch(WelcomeAction.OnCreate) } @Composable diff --git a/core/analytics/src/main/java/com/tangem/core/analytics/Analytics.kt b/core/analytics/src/main/java/com/tangem/core/analytics/Analytics.kt index 20fcb92c92..0e0f40b6c9 100644 --- a/core/analytics/src/main/java/com/tangem/core/analytics/Analytics.kt +++ b/core/analytics/src/main/java/com/tangem/core/analytics/Analytics.kt @@ -54,8 +54,8 @@ object Analytics : GlobalAnalyticsEventHandler { paramsInterceptors[interceptor.id()] = interceptor } - override fun removeParamsInterceptor(interceptor: ParamsInterceptor): ParamsInterceptor? { - return paramsInterceptors.remove(interceptor.id()) + override fun removeParamsInterceptor(interceptorId: String): ParamsInterceptor? { + return paramsInterceptors.remove(interceptorId) } override fun send(event: AnalyticsEvent) { diff --git a/core/analytics/src/main/java/com/tangem/core/analytics/api/ParamsInterceptorApi.kt b/core/analytics/src/main/java/com/tangem/core/analytics/api/ParamsInterceptorApi.kt index e2f9dd5860..683a1c45a0 100644 --- a/core/analytics/src/main/java/com/tangem/core/analytics/api/ParamsInterceptorApi.kt +++ b/core/analytics/src/main/java/com/tangem/core/analytics/api/ParamsInterceptorApi.kt @@ -13,5 +13,5 @@ interface ParamsInterceptor { interface ParamsInterceptorHolder { fun addParamsInterceptor(interceptor: ParamsInterceptor) - fun removeParamsInterceptor(interceptor: ParamsInterceptor): ParamsInterceptor? + fun removeParamsInterceptor(interceptorId: String): ParamsInterceptor? } \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/common/LogConfig.kt b/domain/src/main/java/com/tangem/domain/common/LogConfig.kt index a0046af552..1d1bf6d11a 100644 --- a/domain/src/main/java/com/tangem/domain/common/LogConfig.kt +++ b/domain/src/main/java/com/tangem/domain/common/LogConfig.kt @@ -22,5 +22,5 @@ object NetworkLogConfig { object AnalyticsHandlersLogConfig { val firebase: Boolean = false val appsFlyer: Boolean = false - val amplitude: Boolean = false + val amplitude: Boolean = true } \ No newline at end of file