Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-29 21:49:35 +03:00
parent bb5d6848d9
commit 0ee2f48898
29 changed files with 660 additions and 82 deletions

View file

@ -283,6 +283,8 @@ dependencies {
implementation(projects.features.onboardingV2.impl)
implementation(projects.features.stories.api)
implementation(projects.features.stories.impl)
implementation(projects.features.survey.api)
implementation(projects.features.survey.impl)
implementation(projects.features.txhistory.api)
implementation(projects.features.txhistory.impl)
implementation(projects.features.biometry.api)

View file

@ -210,6 +210,17 @@
android:scheme="tangem" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="survey"
android:scheme="tangem" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

View file

@ -6,6 +6,8 @@ import android.net.Uri
import androidx.core.net.toUri
import com.tangem.common.routing.DeepLinkScheme
import com.tangem.common.uri.ExternalUrlValidator
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.core.navigation.deeplink.DeeplinkLauncher
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.utils.logging.TangemLogger
@ -17,6 +19,7 @@ import com.tangem.utils.logging.TangemLogger
internal class DefaultDeeplinkLauncher(
private val context: Context,
private val urlOpener: UrlOpener,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
) : DeeplinkLauncher {
override fun launch(link: String) {
@ -58,11 +61,33 @@ internal class DefaultDeeplinkLauncher(
}
private fun launchDeepLink(uri: Uri) {
context.startActivity(createDeepLinkIntent(uri))
val intent = createDeepLinkIntent(uri)
if (intent.resolveActivity(context.packageManager) != null) {
context.startActivity(intent)
} else {
TangemLogger.i(
"""
No match found for deep link
|- Received URI: $uri
""".trimIndent(),
)
analyticsExceptionHandler.sendException(
ExceptionAnalyticsEvent(
exception = UnresolvedDeeplinkException(uri),
params = mapOf(
"uri_scheme" to uri.scheme.orEmpty(),
"uri_host" to uri.host.orEmpty(),
),
),
)
}
}
private fun createDeepLinkIntent(uri: Uri): Intent = Intent(Intent.ACTION_VIEW, uri).apply {
setPackage(context.packageName)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
}
}
internal class UnresolvedDeeplinkException(uri: Uri) :
RuntimeException("Deeplink has no matching activity: scheme=${uri.scheme}, host=${uri.host}")

View file

@ -1,6 +1,7 @@
package com.tangem.tap.di
import android.content.Context
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.tap.common.deeplink.DefaultDeeplinkLauncher
import com.tangem.core.navigation.deeplink.DeeplinkLauncher
import com.tangem.core.navigation.finisher.AppFinisher
@ -55,7 +56,10 @@ internal interface UtilsModule {
@Provides
@Singleton
fun provideDeeplinkLauncher(@ApplicationContext context: Context, urlOpener: UrlOpener): DeeplinkLauncher =
DefaultDeeplinkLauncher(context, urlOpener)
fun provideDeeplinkLauncher(
@ApplicationContext context: Context,
urlOpener: UrlOpener,
analyticsExceptionHandler: AnalyticsExceptionHandler,
): DeeplinkLauncher = DefaultDeeplinkLauncher(context, urlOpener, analyticsExceptionHandler)
}
}

View file

@ -21,6 +21,7 @@ import com.tangem.features.feed.entry.components.FeedEntryRoute
import com.tangem.features.home.api.HomeComponent
import com.tangem.features.hotwallet.*
import com.tangem.features.kyc.KycComponent
import com.tangem.features.survey.SurveyComponent
import com.tangem.features.managetokens.component.ChooseManagedTokensComponent
import com.tangem.features.managetokens.component.ManageTokensComponent
import com.tangem.features.managetokens.component.ManageTokensMode
@ -112,6 +113,7 @@ internal class ChildFactory @Inject constructor(
private val tangemPayOnboardingComponentFactory: TangemPayOnboardingComponent.Factory,
private val tangemPayWalletOnboardingComponentFactory: TangemPayHotWalletOnboardingComponent.Factory,
private val kycComponentFactory: KycComponent.Factory,
private val surveyComponentFactory: SurveyComponent.Factory,
private val yieldSupplyEntryComponentFactory: YieldSupplyEntryComponent.Factory,
private val feedEntryComponentFactory: FeedEntryComponent.Factory,
private val addFundsComponentFactory: AddFundsComponent.Factory,
@ -702,6 +704,13 @@ internal class ChildFactory @Inject constructor(
componentFactory = kycComponentFactory,
)
}
is AppRoute.Survey -> {
createComponentChild(
context = context,
params = SurveyComponent.Params(token = route.token, displayId = route.displayId),
componentFactory = surveyComponentFactory,
)
}
is AppRoute.YieldSupplyEntry -> {
createComponentChild(
context = context,

View file

@ -19,6 +19,7 @@ import com.tangem.features.onramp.deeplink.SellDeepLinkHandler
import com.tangem.features.onramp.deeplink.SwapDeepLinkHandler
import com.tangem.features.send.v2.api.deeplink.SellRedirectDeepLinkHandler
import com.tangem.features.staking.api.deeplink.StakingDeepLinkHandler
import com.tangem.features.survey.deeplink.SurveyDeepLinkHandler
import com.tangem.features.tangempay.deeplink.OnboardVisaDeepLinkHandler
import com.tangem.features.tangempay.deeplink.TangemPayMainDeepLinkHandler
import com.tangem.features.tokendetails.deeplink.TokenDetailsDeepLinkHandler
@ -62,6 +63,7 @@ internal class DeepLinkFactory @Inject constructor(
private val newsDeepLink: NewsDeepLinkHandler.Factory,
private val earnDeepLink: EarnDeepLinkHandler.Factory,
private val yieldDeepLink: YieldDeepLinkHandler.Factory,
private val surveyDeepLink: SurveyDeepLinkHandler.Factory,
) {
private val permittedAppRoute = MutableStateFlow(false)
@ -175,6 +177,7 @@ internal class DeepLinkFactory @Inject constructor(
DeepLinkRoute.Earn.host -> earnDeepLink.create(queryParams)
DeepLinkRoute.Yield.host -> yieldDeepLink.create(coroutineScope, queryParams)
DeepLinkRoute.PayAppMain.host -> tangemPayMainDeepLink.create(coroutineScope, queryParams)
DeepLinkRoute.Survey.host -> surveyDeepLink.create(queryParams)
else -> {
TangemLogger.i(
"""

View file

@ -11,6 +11,7 @@ import com.tangem.features.feed.entry.deeplink.MarketsTokenExchangesDeepLinkHand
import com.tangem.features.feed.entry.deeplink.NewsDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.NewsDetailsDeepLinkHandler
import com.tangem.features.feed.entry.deeplink.YieldDeepLinkHandler
import com.tangem.features.survey.deeplink.SurveyDeepLinkHandler
import com.tangem.features.onramp.deeplink.BuyDeepLinkHandler
import com.tangem.features.onramp.deeplink.OnrampDeepLinkHandler
import com.tangem.features.onramp.deeplink.SellDeepLinkHandler
@ -99,6 +100,10 @@ class DeepLinkFactoryTest {
every { create(any()) } returns mockk()
}
private val surveyDeepLinkFactory = mockk<SurveyDeepLinkHandler.Factory>(relaxed = true) {
every { create(any()) } returns mockk()
}
private val earnDeepLinkFactory = mockk<EarnDeepLinkHandler.Factory>(relaxed = true) {
every { create(any()) } returns mockk()
}
@ -140,6 +145,7 @@ class DeepLinkFactoryTest {
newsDeepLink = newsDeepLinkFactory,
earnDeepLink = earnDeepLinkFactory,
yieldDeepLink = yieldDeepLinkFactory,
surveyDeepLink = surveyDeepLinkFactory,
)
@OptIn(ExperimentalCoroutinesApi::class)