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

@ -1,5 +1,6 @@
package com.tangem.features.promobanners.impl.model
import androidx.core.net.toUri
import com.tangem.core.navigation.deeplink.DeeplinkLauncher
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
@ -118,7 +119,18 @@ internal class PromoBannersBlockModel @Inject constructor(
private fun onButtonClick(displayId: Int, deeplink: String?) {
analyticsEventHandler.send(PromoBannerAnalyticsEvent.Clicked(displayId, placeholderName))
deeplink?.let { deeplinkLauncher.launch(it) }
deeplink?.let { deeplinkLauncher.launch(appendSurveyDisplayId(it, displayId)) }
}
private fun appendSurveyDisplayId(deeplink: String, displayId: Int): String {
val uri = deeplink.toUri()
val isSurveyDeeplink = uri.scheme == DEEPLINK_SCHEME_TANGEM && uri.host == DEEPLINK_HOST_SURVEY
if (!isSurveyDeeplink || uri.getQueryParameter(QUERY_DISPLAY_ID) != null) return deeplink
return uri.buildUpon()
.appendQueryParameter(QUERY_DISPLAY_ID, displayId.toString())
.build()
.toString()
}
private fun getInitialState() = PromoBannersBlockUM(
@ -152,4 +164,10 @@ internal class PromoBannersBlockModel @Inject constructor(
}
}
}
private companion object {
const val DEEPLINK_SCHEME_TANGEM = "tangem"
const val DEEPLINK_HOST_SURVEY = "survey"
const val QUERY_DISPLAY_ID = "display_id"
}
}

View file

@ -0,0 +1,14 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
id("configuration")
}
android {
namespace = "com.tangem.features.survey.api"
}
dependencies {
implementation(projects.core.decompose)
implementation(projects.core.ui)
}

View file

@ -0,0 +1,11 @@
package com.tangem.features.survey
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
interface SurveyComponent : ComposableContentComponent {
data class Params(val token: String, val displayId: String?)
interface Factory : ComponentFactory<Params, SurveyComponent>
}

View file

@ -0,0 +1,6 @@
package com.tangem.features.survey
interface SurveyFeatureToggles {
val areSurveysEnabled: Boolean
}

View file

@ -0,0 +1,14 @@
package com.tangem.features.survey
import android.app.Activity
interface SurveySparrowLauncher {
fun present(activity: Activity, data: SurveyLaunchData)
}
data class SurveyLaunchData(
val domain: String,
val token: String,
val customParams: Map<String, String>,
)

View file

@ -0,0 +1,8 @@
package com.tangem.features.survey.deeplink
interface SurveyDeepLinkHandler {
interface Factory {
fun create(queryParams: Map<String, String>): SurveyDeepLinkHandler
}
}

View file

@ -0,0 +1,61 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.hilt.android)
id("configuration")
}
android {
namespace = "com.tangem.features.survey.impl"
}
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
dependencies {
/* Project - API */
implementation(projects.features.survey.api)
/* Domain */
implementation(projects.domain.common)
implementation(projects.domain.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
/* Core */
implementation(projects.core.analytics)
implementation(projects.core.analytics.models)
implementation(projects.core.configToggles)
implementation(projects.core.datasource)
implementation(projects.core.decompose)
implementation(projects.core.ui)
implementation(projects.core.utils)
/* Common */
implementation(projects.common.routing)
/* DI */
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
/* Compose */
implementation(deps.compose.runtime)
implementation(deps.compose.ui)
/* Other */
implementation(deps.kotlin.coroutines)
implementation(deps.arrow.core)
/** Tangem libraries */
implementation(tangemDeps.card.core)
implementation(deps.surveysparrow)
/** Tests */
testImplementation(deps.test.junit5)
testRuntimeOnly(deps.test.junit5.engine)
testImplementation(deps.test.mockk)
testImplementation(deps.test.truth)
testImplementation(deps.test.coroutine)
}

View file

@ -0,0 +1,73 @@
package com.tangem.features.survey.impl
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.features.survey.SurveyComponent
import com.tangem.features.survey.SurveyLaunchData
import com.tangem.features.survey.SurveySparrowLauncher
import com.tangem.features.survey.impl.service.SurveyCustomParamsBuilder
import com.tangem.utils.logging.TangemLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.launch
@Suppress("LongParameterList")
internal class DefaultSurveyComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: SurveyComponent.Params,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val customParamsBuilder: SurveyCustomParamsBuilder,
private val surveySparrowLauncher: SurveySparrowLauncher,
@Suppress("UnusedPrivateProperty") // TODO([REDACTED_TASK_KEY]): emit [Survey] analytics events
private val analyticsEventHandler: AnalyticsEventHandler,
) : SurveyComponent, AppComponentContext by appComponentContext {
init {
// componentScope runs on mainImmediate, so presenting the SDK is already on the main thread.
componentScope.launch {
val launchData = buildLaunchData()
if (launchData != null) {
surveySparrowLauncher.present(activity, launchData)
// TODO([REDACTED_TASK_KEY]): analyticsEventHandler.send(SurveyAnalyticsEvent.Shown(...))
}
router.pop()
}
}
private suspend fun buildLaunchData(): SurveyLaunchData? {
return getSelectedWalletSyncUseCase().fold(
ifLeft = { error ->
TangemLogger.e("$TAG: survey skipped, no available wallet ($error)")
null
},
ifRight = { userWallet ->
SurveyLaunchData(
domain = SURVEY_DOMAIN,
token = params.token,
customParams = customParamsBuilder.build(
userWallet = userWallet,
token = params.token,
displayId = params.displayId,
),
)
},
)
}
@Composable
override fun Content(modifier: Modifier) = Unit
@AssistedFactory
interface Factory : SurveyComponent.Factory {
override fun create(context: AppComponentContext, params: SurveyComponent.Params): DefaultSurveyComponent
}
private companion object {
const val TAG = "SurveyComponent"
const val SURVEY_DOMAIN = "tangem.surveysparrow.com"
}
}

View file

@ -0,0 +1,14 @@
package com.tangem.features.survey.impl
import com.tangem.core.configtoggle.FeatureToggles
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.survey.SurveyFeatureToggles
import javax.inject.Inject
internal class DefaultSurveyFeatureToggles @Inject constructor(
private val featureTogglesManager: FeatureTogglesManager,
) : SurveyFeatureToggles {
override val areSurveysEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.AND_15482_SURVEYSPARROW_ENABLED)
}

View file

@ -0,0 +1,38 @@
package com.tangem.features.survey.impl
import android.app.Activity
import com.surveysparrow.ss_android_sdk.SsSurvey
import com.surveysparrow.ss_android_sdk.SurveySparrow
import com.tangem.features.survey.SurveyLaunchData
import com.tangem.features.survey.SurveySparrowLauncher
import com.tangem.utils.logging.TangemLogger
import javax.inject.Inject
internal class DefaultSurveySparrowLauncher @Inject constructor() : SurveySparrowLauncher {
override fun present(activity: Activity, data: SurveyLaunchData) {
if (activity.isFinishing || activity.isDestroyed) {
TangemLogger.e("$TAG: cannot present survey, activity is finishing/destroyed")
return
}
val survey = try {
SsSurvey(data.domain, data.token).apply {
setSurveyType(SurveySparrow.CLASSIC)
data.customParams.forEach { (key, value) -> addCustomParam(key, value) }
}
} catch (e: Exception) {
TangemLogger.e("$TAG: failed to create SurveySparrow survey", e)
return
}
// Result handling (onActivityResult -> [Survey] Completed/Dismissed) is planned in [REDACTED_TASK_KEY]
SurveySparrow(activity, survey).startSurveyForResult(SURVEY_REQUEST_CODE)
TangemLogger.d("$TAG: survey started (requestCode=$SURVEY_REQUEST_CODE)")
}
private companion object {
const val TAG = "SurveySparrowPresenter"
const val SURVEY_REQUEST_CODE = 1001
}
}

View file

@ -0,0 +1,47 @@
package com.tangem.features.survey.impl.deeplink
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.features.survey.SurveyFeatureToggles
import com.tangem.features.survey.deeplink.SurveyDeepLinkHandler
import com.tangem.utils.logging.TangemLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultSurveyDeepLinkHandler @AssistedInject constructor(
@Assisted private val queryParams: Map<String, String>,
private val surveyFeatureToggles: SurveyFeatureToggles,
private val appRouter: AppRouter,
) : SurveyDeepLinkHandler {
init {
handleDeepLink()
}
private fun handleDeepLink() {
if (!surveyFeatureToggles.areSurveysEnabled) {
TangemLogger.i("$TAG: survey deeplink ignored, feature is disabled")
return
}
val token = queryParams[QUERY_TOKEN]?.takeIf { it.isNotBlank() }
if (token == null) {
TangemLogger.e("$TAG: survey deeplink ignored, missing 'token' query param")
return
}
appRouter.push(AppRoute.Survey(token = token, displayId = queryParams[QUERY_DISPLAY_ID]))
}
@AssistedFactory
interface Factory : SurveyDeepLinkHandler.Factory {
override fun create(queryParams: Map<String, String>): DefaultSurveyDeepLinkHandler
}
private companion object {
const val TAG = "SurveyDeepLink"
const val QUERY_TOKEN = "token"
const val QUERY_DISPLAY_ID = "display_id"
}
}

View file

@ -0,0 +1,36 @@
package com.tangem.features.survey.impl.di
import com.tangem.features.survey.SurveyComponent
import com.tangem.features.survey.SurveyFeatureToggles
import com.tangem.features.survey.SurveySparrowLauncher
import com.tangem.features.survey.deeplink.SurveyDeepLinkHandler
import com.tangem.features.survey.impl.DefaultSurveyComponent
import com.tangem.features.survey.impl.DefaultSurveyFeatureToggles
import com.tangem.features.survey.impl.DefaultSurveySparrowLauncher
import com.tangem.features.survey.impl.deeplink.DefaultSurveyDeepLinkHandler
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface SurveyModule {
@Binds
@Singleton
fun bindSurveyFeatureToggles(impl: DefaultSurveyFeatureToggles): SurveyFeatureToggles
@Binds
@Singleton
fun bindSurveySparrowLauncher(impl: DefaultSurveySparrowLauncher): SurveySparrowLauncher
@Binds
@Singleton
fun bindSurveyComponentFactory(impl: DefaultSurveyComponent.Factory): SurveyComponent.Factory
@Binds
@Singleton
fun bindSurveyDeepLinkHandlerFactory(impl: DefaultSurveyDeepLinkHandler.Factory): SurveyDeepLinkHandler.Factory
}

View file

@ -0,0 +1,48 @@
package com.tangem.features.survey.impl.service
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.core.analytics.AppInstanceIdProvider
import com.tangem.datasource.api.tangemTech.models.WalletType
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.utils.SupportedLanguages
import com.tangem.utils.info.AppInfoProvider
import javax.inject.Inject
internal class SurveyCustomParamsBuilder @Inject constructor(
private val appInstanceIdProvider: AppInstanceIdProvider,
private val appInfoProvider: AppInfoProvider,
) {
suspend fun build(userWallet: UserWallet, token: String, displayId: String?): Map<String, String> {
return buildMap {
put(KEY_SURVEY_KEY, token)
put(KEY_WALLET_ID, hashWalletId(userWallet))
WalletType.from(userWallet)?.let { put(KEY_WALLET_TYPE, it.name.lowercase()) }
displayId?.takeIf { it.isNotBlank() }?.let { put(KEY_DISPLAY_ID, it) }
appInstanceIdProvider.getAppInstanceId()?.let { put(KEY_DEVICE_ID, it) }
put(KEY_PLATFORM, appInfoProvider.platform.lowercase())
put(KEY_APP_VERSION, appInfoProvider.appVersion)
put(KEY_LANGUAGE, SupportedLanguages.getCurrentSupportedLanguageCode())
}
}
private fun hashWalletId(userWallet: UserWallet): String {
return userWallet.walletId.stringValue
.hexToBytes()
.calculateSha256()
.toHexString()
}
private companion object {
const val KEY_SURVEY_KEY = "survey_key"
const val KEY_WALLET_ID = "wallet_id"
const val KEY_WALLET_TYPE = "wallet_type"
const val KEY_DISPLAY_ID = "display_id"
const val KEY_DEVICE_ID = "device_id"
const val KEY_PLATFORM = "platform"
const val KEY_APP_VERSION = "app_version"
const val KEY_LANGUAGE = "language"
}
}

View file

@ -0,0 +1,81 @@
package com.tangem.features.survey.impl.deeplink
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.features.survey.SurveyFeatureToggles
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkObject
import io.mockk.verify
import com.tangem.utils.logging.TangemLogger
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
internal class DefaultSurveyDeepLinkHandlerTest {
private val featureToggles = mockk<SurveyFeatureToggles>()
private val appRouter = mockk<AppRouter>(relaxed = true)
@BeforeEach
fun setup() {
mockkObject(TangemLogger)
every { TangemLogger.i(any()) } just Runs
every { TangemLogger.e(any()) } just Runs
}
@AfterEach
fun tearDown() {
unmockkObject(TangemLogger)
}
@Test
fun `does not navigate when feature is disabled`() {
every { featureToggles.areSurveysEnabled } returns false
createHandler(mapOf("token" to TOKEN, "display_id" to DISPLAY_ID))
verify(exactly = 0) { appRouter.push(any(), any()) }
}
@Test
fun `does not navigate when token is missing`() {
every { featureToggles.areSurveysEnabled } returns true
createHandler(emptyMap())
verify(exactly = 0) { appRouter.push(any(), any()) }
}
@Test
fun `pushes survey route with token and display id on happy path`() {
every { featureToggles.areSurveysEnabled } returns true
createHandler(mapOf("token" to TOKEN, "display_id" to DISPLAY_ID))
verify { appRouter.push(route = AppRoute.Survey(token = TOKEN, displayId = DISPLAY_ID), onComplete = any()) }
}
@Test
fun `pushes survey route with null display id when absent`() {
every { featureToggles.areSurveysEnabled } returns true
createHandler(mapOf("token" to TOKEN))
verify { appRouter.push(route = AppRoute.Survey(token = TOKEN, displayId = null), onComplete = any()) }
}
private fun createHandler(queryParams: Map<String, String>) = DefaultSurveyDeepLinkHandler(
queryParams = queryParams,
surveyFeatureToggles = featureToggles,
appRouter = appRouter,
)
private companion object {
const val TOKEN = "ntt-84iF22PDajmervYneMW4kv"
const val DISPLAY_ID = "42"
}
}

View file

@ -0,0 +1,104 @@
package com.tangem.features.survey.impl.service
import com.google.common.truth.Truth.assertThat
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.core.analytics.AppInstanceIdProvider
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.info.AppInfoProvider
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.util.Locale
internal class SurveyCustomParamsBuilderTest {
private val appInstanceIdProvider = mockk<AppInstanceIdProvider>()
private val appInfoProvider = mockk<AppInfoProvider>()
private val builder = SurveyCustomParamsBuilder(
appInstanceIdProvider = appInstanceIdProvider,
appInfoProvider = appInfoProvider,
)
@BeforeEach
fun setup() {
Locale.setDefault(Locale.ENGLISH)
every { appInfoProvider.platform } returns "Android"
every { appInfoProvider.appVersion } returns "5.40"
coEvery { appInstanceIdProvider.getAppInstanceId() } returns "device-123"
}
@Test
fun `builds all params for a cold wallet`() = runTest {
val wallet = coldWallet(WALLET_ID_HEX)
val params = builder.build(userWallet = wallet, token = TOKEN, displayId = "42")
assertThat(params).containsExactlyEntriesIn(
mapOf(
"survey_key" to TOKEN,
"wallet_id" to expectedWalletIdHash(WALLET_ID_HEX),
"wallet_type" to "cold",
"display_id" to "42",
"device_id" to "device-123",
"platform" to "android",
"app_version" to "5.40",
"language" to "en",
),
)
}
@Test
fun `wallet_id hash is uppercase hex`() = runTest {
val params = builder.build(userWallet = coldWallet(WALLET_ID_HEX), token = TOKEN, displayId = null)
val walletId = params.getValue("wallet_id")
assertThat(walletId).isEqualTo(walletId.uppercase())
assertThat(walletId).matches("[0-9A-F]+")
}
@Test
fun `wallet_type is hot for a hot wallet`() = runTest {
val wallet = mockk<UserWallet.Hot> { every { walletId } returns UserWalletId(WALLET_ID_HEX) }
val params = builder.build(userWallet = wallet, token = TOKEN, displayId = null)
assertThat(params["wallet_type"]).isEqualTo("hot")
}
@Test
fun `device_id is omitted when app instance id is null`() = runTest {
coEvery { appInstanceIdProvider.getAppInstanceId() } returns null
val params = builder.build(userWallet = coldWallet(WALLET_ID_HEX), token = TOKEN, displayId = "42")
assertThat(params).doesNotContainKey("device_id")
}
@Test
fun `display_id is omitted when null or blank`() = runTest {
val nullCase = builder.build(userWallet = coldWallet(WALLET_ID_HEX), token = TOKEN, displayId = null)
val blankCase = builder.build(userWallet = coldWallet(WALLET_ID_HEX), token = TOKEN, displayId = " ")
assertThat(nullCase).doesNotContainKey("display_id")
assertThat(blankCase).doesNotContainKey("display_id")
}
private fun coldWallet(walletIdHex: String): UserWallet.Cold = mockk {
every { walletId } returns UserWalletId(walletIdHex)
}
private fun expectedWalletIdHash(walletIdHex: String): String =
walletIdHex.hexToBytes().calculateSha256().toHexString()
private companion object {
const val TOKEN = "ntt-84iF22PDajmervYneMW4kv"
const val WALLET_ID_HEX = "0123456789ABCDEF"
}
}

View file

@ -47,7 +47,6 @@ dependencies {
/** Other libraries */
implementation(deps.arrow.core)
implementation(deps.kotlin.immutable.collections)
implementation(deps.surveysparrow)
/** Core modules */
implementation(projects.core.datasource)
@ -60,6 +59,7 @@ dependencies {
/** Feature Apis */
implementation(projects.features.tester.api)
implementation(projects.features.pushNotifications.api)
implementation(projects.features.survey.api)
/* SDK */
implementation(tangemDeps.blockchain)

View file

@ -1,6 +1,5 @@
package com.tangem.feature.tester.presentation
import android.widget.Toast
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -18,7 +17,6 @@ import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.screen.ComposeActivity
import com.tangem.datasource.local.config.environment.EnvironmentConfig
import com.tangem.feature.tester.presentation.accounts.ui.AccountsScreen
import com.tangem.feature.tester.presentation.accounts.viewmodel.TesterAccountsViewModel
import com.tangem.feature.tester.presentation.actions.TesterActionsScreen
@ -40,9 +38,10 @@ import com.tangem.feature.tester.presentation.providers.ui.BlockchainProvidersSc
import com.tangem.feature.tester.presentation.providers.viewmodel.BlockchainProvidersViewModel
import com.tangem.feature.tester.presentation.storybook.ui.StoryBookScreen
import com.tangem.feature.tester.presentation.storybook.viewmodel.StoryBookViewModel
import com.tangem.feature.tester.presentation.surveysparrow.SurveySparrowManager
import com.tangem.feature.tester.presentation.testpush.ui.TestPushScreen
import com.tangem.feature.tester.presentation.testpush.viewmodel.TestPushViewModel
import com.tangem.features.survey.SurveyLaunchData
import com.tangem.features.survey.SurveySparrowLauncher
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.collections.immutable.persistentSetOf
import javax.inject.Inject
@ -64,7 +63,7 @@ internal class TesterActivity : ComposeActivity() {
lateinit var appRouter: AppRouter
@Inject
lateinit var environmentConfig: EnvironmentConfig
lateinit var surveySparrowLauncher: SurveySparrowLauncher
@Composable
override fun ScreenContent(modifier: Modifier) {
@ -217,29 +216,16 @@ internal class TesterActivity : ComposeActivity() {
}
private fun startSurveySparrow(): Boolean {
val token = environmentConfig.surveySparrowToken
if (token.isNullOrEmpty()) {
val toast = Toast.makeText(
this,
"Survey Sparrow is not configured. Token is missing.",
Toast.LENGTH_LONG,
)
toast.show()
return false
}
SurveySparrowManager(domain = DOMAIN, token = token).startSurveyForResult(
surveySparrowLauncher.present(
activity = this,
requestCode = SURVEY_SPARROW_REQUEST_CODE,
data = SurveyLaunchData(domain = DOMAIN, token = TEST_SHARE_TOKEN, customParams = emptyMap()),
)
return true
}
private companion object {
const val DOMAIN = "tangem.com"
const val SURVEY_SPARROW_REQUEST_CODE = 1001
const val DOMAIN = "tangem.surveysparrow.com"
const val TEST_SHARE_TOKEN = "ntt-84iF22PDajmervYneMW4kv"
}
}

View file

@ -1,55 +0,0 @@
package com.tangem.feature.tester.presentation.surveysparrow
import android.app.Activity
import com.surveysparrow.ss_android_sdk.SsSurvey
import com.surveysparrow.ss_android_sdk.SurveySparrow
import com.tangem.utils.logging.TangemLogger
/**
* Manager for Survey Sparrow SDK.
*
* @param domain Survey Sparrow domain (e.g., "yourcompany")
* @param token Survey Sparrow SDK token
*/
class SurveySparrowManager(
private val domain: String,
private val token: String,
) {
/**
* Create a SurveySparrow instance to start a survey.
*
* @param activity The activity context
* @param customVariables Optional custom variables to pass to the survey
* @return SurveySparrow instance ready to start
*/
fun createSurvey(activity: Activity, customVariables: Map<String, String>? = null): SurveySparrow? {
return try {
val survey = SsSurvey(domain, token).apply {
customVariables?.forEach { (key, value) ->
addCustomParam(key, value)
}
}
SurveySparrow(activity, survey)
} catch (e: Exception) {
TangemLogger.e("Failed to create SurveySparrow survey", e)
null
}
}
/**
* Start a survey for result.
*
* @param activity The activity context
* @param requestCode The request code for onActivityResult
* @param customVariables Optional custom variables to pass to the survey
*/
fun startSurveyForResult(activity: Activity, requestCode: Int, customVariables: Map<String, String>? = null) {
val surveySparrow = createSurvey(activity, customVariables)
if (surveySparrow != null) {
surveySparrow.startSurveyForResult(requestCode)
TangemLogger.d("SurveySparrow survey started with requestCode: $requestCode")
}
}
}