Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-26 18:44:37 +04:00
parent 692c6514fc
commit bba915b9ec
10 changed files with 138 additions and 0 deletions

View file

@ -66,6 +66,7 @@ import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
import com.tangem.tap.common.analytics.handlers.appsflyer.AppsFlyerAnalyticsHandler
import com.tangem.tap.common.analytics.handlers.appsflyer.AppsFlyerClient
import com.tangem.tap.common.analytics.handlers.customerio.CustomerIoAnalyticsHandler
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
import com.tangem.tap.common.images.createCoilImageLoader
import com.tangem.tap.common.log.TangemAppLoggerInitializer
@ -410,6 +411,7 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
factory.addHandlerBuilder(AmplitudeAnalyticsHandler.Builder())
factory.addHandlerBuilder(FirebaseAnalyticsHandler.Builder())
factory.addHandlerBuilder(AppsFlyerAnalyticsHandler.Builder(appsFlyerClientFactory))
factory.addHandlerBuilder(CustomerIoAnalyticsHandler.Builder())
factory.addFilter(oneTimeEventFilter)
factory.addFilter(AppsFlyerEventFilter())

View file

@ -0,0 +1,8 @@
package com.tangem.tap.common.analytics.handlers.customerio
import com.tangem.core.analytics.api.UserIdHolder
/**
* Client interface for Customer.io SDK operations.
*/
interface CustomerIoAnalyticsClient : UserIdHolder

View file

@ -0,0 +1,50 @@
package com.tangem.tap.common.analytics.handlers.customerio
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.AnalyticsUserIdHandler
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
/**
* Customer.io analytics handler.
*/
class CustomerIoAnalyticsHandler(
private val client: CustomerIoAnalyticsClient,
) : AnalyticsHandler, AnalyticsUserIdHandler {
override fun id(): String = ID
override fun setUserId(userId: String) {
client.setUserId(userId)
}
override fun clearUserId() {
client.clearUserId()
}
override fun send(event: AnalyticsEvent) {
// No-op: product events are not sent to Customer.io.
// Triggers are configured to come from Amplitude directly.
}
companion object {
const val ID = "CustomerIO"
}
class Builder : AnalyticsHandlerBuilder {
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? {
return if (data.logConfig.isCustomerIoLogEnabled) {
CustomerIoAnalyticsHandler(client = CustomerIoLogClient())
} else if (data.config.customerIoCdpApiKey.isNotBlank()) {
CustomerIoAnalyticsHandler(
client = CustomerIoClient(
application = data.application,
cdpApiKey = data.config.customerIoCdpApiKey,
),
)
} else {
null
}
}
}
}

View file

@ -0,0 +1,43 @@
package com.tangem.tap.common.analytics.handlers.customerio
import android.app.Application
import io.customer.messagingpush.ModuleMessagingPushFCM
import io.customer.sdk.CustomerIO
import io.customer.sdk.CustomerIOBuilder
import timber.log.Timber
/**
* Real Customer.io SDK client.
*
* Initializes the SDK with the given CDP API key and provides:
* - User identification (identify / clearIdentify)
*
* Auto-tracking of application lifecycle events is disabled since it is not needed.
* Auto-tracking of screen views is disabled.
*/
internal class CustomerIoClient(
application: Application,
cdpApiKey: String,
) : CustomerIoAnalyticsClient {
init {
CustomerIOBuilder(
applicationContext = application,
cdpApiKey = cdpApiKey,
)
.trackApplicationLifecycleEvents(false)
.autoTrackActivityScreens(false)
.addCustomerIOModule(ModuleMessagingPushFCM())
.build()
Timber.d("CustomerIO SDK initialized")
}
override fun setUserId(userId: String) {
CustomerIO.instance().identify(userId = userId)
}
override fun clearUserId() {
CustomerIO.instance().clearIdentify()
}
}

View file

@ -0,0 +1,23 @@
package com.tangem.tap.common.analytics.handlers.customerio
import timber.log.Timber
/**
* Log client for Customer.io (used in debug mode).
*
* Logs all operations to Timber instead of sending them to Customer.io.
*/
internal class CustomerIoLogClient : CustomerIoAnalyticsClient {
private var userId: String? = null
override fun setUserId(userId: String) {
this.userId = userId
Timber.tag(CustomerIoAnalyticsHandler.ID).d("identify: userId=$userId")
}
override fun clearUserId() {
Timber.tag(CustomerIoAnalyticsHandler.ID).d("clearIdentify: previous userId=$userId")
this.userId = null
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.tap.common.pushes
import android.annotation.SuppressLint
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.customer.messagingpush.CustomerIOFirebaseMessagingService
import timber.log.Timber
@SuppressLint("MissingFirebaseInstanceTokenRefresh")
@ -15,11 +16,15 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
Timber.d("New FCM token received: $token")
CustomerIOFirebaseMessagingService.onNewToken(applicationContext, token)
}
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
CustomerIOFirebaseMessagingService.onMessageReceived(applicationContext, message)
val notification = message.notification ?: return
val channelId = notification.channelId ?: TANGEM_CHANNEL_ID