Skip to content

Commit

Permalink
Merge pull request #459 from mysteriumnetwork/feature/Replace_matomo_…
Browse files Browse the repository at this point in the history
…analitics

Replace matomo analitics
  • Loading branch information
ArtemHryhorovGeniusee authored Oct 27, 2021
2 parents 778b1fa + f13f1b2 commit 797e7e6
Show file tree
Hide file tree
Showing 26 changed files with 554 additions and 210 deletions.
4 changes: 0 additions & 4 deletions android/app/src/main/java/updated/mysterium/vpn/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import network.mysterium.vpn.BuildConfig
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import updated.mysterium.vpn.analitics.AnalyticEvent
import updated.mysterium.vpn.analitics.AnalyticWrapper
import updated.mysterium.vpn.common.countries.Countries
import updated.mysterium.vpn.common.localisation.LocaleHelper.onAttach
import updated.mysterium.vpn.core.MysteriumAndroidCoreService
Expand All @@ -37,7 +35,6 @@ class App : Application(), LifecycleObserver {
}

val deferredMysteriumCoreService = CompletableDeferred<MysteriumCoreService>()
private val analyticWrapper: AnalyticWrapper by inject()
private val allNodesViewModel: AllNodesViewModel by inject()
private val exchangeRateViewModel: ExchangeRateViewModel by inject()

Expand All @@ -51,7 +48,6 @@ class App : Application(), LifecycleObserver {
modules(Modules.main)
}
bindMysteriumService()
analyticWrapper.track(AnalyticEvent.LOGIN)
}

@OnLifecycleEvent(Lifecycle.Event.ON_START)
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package updated.mysterium.vpn.analytics

enum class AnalyticEvent(val eventName: String) {
STARTUP("startup"),
CONNECT_ATTEMPT("connect_attempt"),
CONNECT_SUCCESS("connect_success"),
CONNECT_FAILURE("connect_failure"),
MANUAL_CONNECT("manual_connect"),
QUICK_CONNECT("quick_connect"),
DISCONNECT_ATTEMPT("disconnect_attempt"),
DISCONNECT_SUCCESS("disconnect_success"),
DISCONNECT_FAILURE("disconnect_failure"),
PAGE_VIEW("page_view"),
BALANCE_UPDATE("balance_update")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package updated.mysterium.vpn.analytics

import android.util.Log
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import updated.mysterium.vpn.analytics.mysterium.MysteriumAnalyticService
import updated.mysterium.vpn.model.analytics.ClientAnalyticRequest
import updated.mysterium.vpn.model.analytics.EventAnalyticRequest

class AnalyticWrapper {

private companion object {
const val SUCCESS_TRACK_CODE = 202
const val TAG = "AnalyticWrapper"
}

private var apiInterface = MysteriumAnalyticService.analyticService

fun trackEvent(event: ClientAnalyticRequest, retry: Boolean = false) {
Log.i(TAG, event.toString())
val call = apiInterface.trackEvent(event)
call?.enqueue(
object : Callback<Unit?> {

override fun onResponse(call: Call<Unit?>, response: Response<Unit?>) {
if (response.code() != SUCCESS_TRACK_CODE && retry.not()) {
trackEvent(event, true)
}
}

override fun onFailure(call: Call<Unit?>, throwable: Throwable) {
Log.i(TAG, throwable.localizedMessage ?: throwable.toString())
if (retry.not()) {
trackEvent(event, true)
}
}
}
)
}

fun trackEvent(event: EventAnalyticRequest, retry: Boolean = false) {
Log.i(TAG, event.toString())
val call = apiInterface.trackEvent(event)
call?.enqueue(
object : Callback<Unit?> {

override fun onResponse(call: Call<Unit?>, response: Response<Unit?>) {
if (response.code() != SUCCESS_TRACK_CODE && retry.not()) {
trackEvent(event, true)
}
}

override fun onFailure(call: Call<Unit?>, throwable: Throwable) {
Log.i(TAG, throwable.localizedMessage ?: throwable.toString())
if (retry.not()) {
trackEvent(event, true)
}
}
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package updated.mysterium.vpn.analytics.mysterium

import android.content.Context
import android.util.Log
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import mysterium.GetBalanceRequest
import updated.mysterium.vpn.analytics.AnalyticEvent
import updated.mysterium.vpn.analytics.AnalyticWrapper
import updated.mysterium.vpn.common.data.DeviceUtil
import updated.mysterium.vpn.model.analytics.ClientAnalyticRequest
import updated.mysterium.vpn.model.analytics.ClientInfo
import updated.mysterium.vpn.model.analytics.EventAnalyticRequest
import updated.mysterium.vpn.model.manual.connect.Proposal
import updated.mysterium.vpn.model.wallet.IdentityModel
import updated.mysterium.vpn.model.wallet.IdentityRegistrationStatus
import updated.mysterium.vpn.network.provider.usecase.UseCaseProvider

class MysteriumAnalytic(
context: Context,
private val analyticWrapper: AnalyticWrapper,
useCaseProvider: UseCaseProvider
) {

private companion object {
const val TAG = "MysteriumAnalytic"
}

val eventTracked: SharedFlow<String>
get() = _eventTracked
private val _eventTracked = MutableSharedFlow<String>()

private val connectionUseCase = useCaseProvider.connection()
private val balanceUseCase = useCaseProvider.balance()
private var balanceRequest: GetBalanceRequest? = null

private val machineID = DeviceUtil.getDeviceID(context.contentResolver)
private val appVersion = DeviceUtil.getAppVersion(context)
private val osVersion = DeviceUtil.getAndroidVersion()
private val country = DeviceUtil.getConfiguredCountry(context)
private val scope = CoroutineScope(Dispatchers.IO)

fun trackEvent(
eventName: String,
pageTitle: String? = null,
proposal: Proposal? = null
) {
val handler = CoroutineExceptionHandler { _, exception ->
Log.e(TAG, exception.localizedMessage ?: exception.toString())
_eventTracked.tryEmit(eventName)
}

scope.launch(handler) {
requestEventTracking(eventName, pageTitle, proposal)
}
}

private suspend fun requestEventTracking(
eventName: String,
pageTitle: String? = null,
proposal: Proposal? = null
) {
if (eventName == AnalyticEvent.STARTUP.eventName) {
val clientInfo = getClientInfo()
val clientAnalyticRequest = ClientAnalyticRequest(
eventName,
clientInfo
)
analyticWrapper.trackEvent(clientAnalyticRequest)
_eventTracked.emit(eventName)
} else {
val analyticRequest = getAnalyticRequest(
eventName, proposal, pageTitle
)
analyticWrapper.trackEvent(analyticRequest)
_eventTracked.emit(eventName)
}
}

private fun getClientInfo(): ClientInfo {
val identityAddress = connectionUseCase.getSavedIdentityAddress()
return ClientInfo(
machineID = machineID,
appVersion = appVersion,
osVersion = osVersion,
country = country,
consumerID = identityAddress
)
}

private suspend fun getAnalyticRequest(
eventName: String,
proposal: Proposal?,
pageTitle: String?
): EventAnalyticRequest {

// client
val clientInfo = getClientInfo()

// duration
var duration: Long? = connectionUseCase.getDuration()
if (duration == 0L) {
duration = null
}

// balance
initBalanceRequest()
var balance: Double? = null
balanceRequest?.let {
balance = balanceUseCase.getBalance(it)
}

// country, providerID
val country = proposal?.countryName
val providerID = proposal?.providerID

return EventAnalyticRequest(
eventName = eventName,
duration = duration,
balance = balance,
country = country,
providerID = providerID,
pageTitle = pageTitle,
clientInfo = clientInfo
)
}

private suspend fun initBalanceRequest() {
val nodeIdentity = connectionUseCase.getIdentity()
val identity = IdentityModel(
address = nodeIdentity.address,
channelAddress = nodeIdentity.channelAddress,
status = IdentityRegistrationStatus.parse(nodeIdentity.registrationStatus)
)
balanceRequest = GetBalanceRequest().apply {
identityAddress = identity.address
}
}
}
Loading

0 comments on commit 797e7e6

Please sign in to comment.