Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion app/src/main/java/one/mixin/android/MixinApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import one.mixin.android.util.BiometricUtil
import one.mixin.android.ui.web.clips
import one.mixin.android.ui.web.refresh
import one.mixin.android.ui.web.releaseAll
import one.mixin.android.util.analytics.AnalyticsTracker
import one.mixin.android.util.CursorWindowFixer
import one.mixin.android.util.MemoryCallback
import one.mixin.android.util.debug.FileLogTree
Expand Down Expand Up @@ -214,6 +215,9 @@ open class MixinApplication :
AppsFlyerLib.getInstance().init(BuildConfig.APPSFLYER_DEV_KEY, object : AppsFlyerConversionListener {
override fun onConversionDataSuccess(conversionData: Map<String, Any>) {
Timber.d("AppsFlyer Conversion Data: $conversionData")
if (Session.checkToken()) {
AnalyticsTracker.updateAppsFlyerConversionUserProperties(conversionData)
}
}

override fun onConversionDataFail(error: String) {
Expand All @@ -228,7 +232,7 @@ open class MixinApplication :
Timber.e("AppsFlyer Attribution Failure: $error")
}
}, this)
AppsFlyerLib.getInstance().start(this)
Session.getAccount()?.let { AnalyticsTracker.setAppsFlyerCustomerUserId(it) }
val firebaseAnalytics = FirebaseAnalytics.getInstance(this)
val appInstanceIdTask = firebaseAnalytics.appInstanceId
val sessionIdTask = firebaseAnalytics.sessionId
Expand All @@ -247,6 +251,13 @@ open class MixinApplication :
}
}

private fun startAppsFlyer(activity: Activity) {
if (BuildConfig.APPSFLYER_DEV_KEY.isBlank()) {
return
}
AppsFlyerLib.getInstance().start(activity)
}

override fun onConfigurationChanged(newConfig: android.content.res.Configuration) {
super.onConfigurationChanged(newConfig)
val activity = topActivity
Expand Down Expand Up @@ -426,6 +437,7 @@ open class MixinApplication :
appAuthShown = true
}
if (activityReferences == 1 && activity !is AppAuthActivity && !isActivityChangingConfigurations) {
startAppsFlyer(activity)
checkAndShowAppAuth(activity)
}
}
Expand Down
11 changes: 10 additions & 1 deletion app/src/main/java/one/mixin/android/job/DecryptMessage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ import org.whispersystems.libsignal.SignalProtocolAddress
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.math.BigDecimal
import java.util.UUID

class DecryptMessage(private val lifecycleScope: CoroutineScope) : Injector() {
Expand Down Expand Up @@ -1194,7 +1195,15 @@ class DecryptMessage(private val lifecycleScope: CoroutineScope) : Injector() {
insertMessage(message, data)
jobManager.addJobInBackground(RefreshTokensJob(snapshot.assetId, data.conversationId, data.messageId))
jobManager.addJobInBackground(SyncOutputJob())
runBlocking { AnalyticsTracker.setAssetLevel(tokenDao.findTotalUSDBalance() ?: 0) }
runBlocking {
AnalyticsTracker.setAssetLevel(tokenDao.findTotalUSDBalance() ?: 0)
val receivedAmount = snapshot.amount.toBigDecimalOrNull()
if (receivedAmount != null && receivedAmount > BigDecimal.ZERO) {
val token = tokenDao.simpleAsset(snapshot.assetId)
val priceUsd = token?.priceUsd?.toBigDecimalOrNull() ?: BigDecimal.ZERO
AnalyticsTracker.trackAssetReceiveSuccess(token?.symbol, receivedAmount.multiply(priceUsd))
}
}

if (snapshot.amount.toFloat() > 0) {
generateNotification(message, data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import one.mixin.android.extension.decodeBase64
import one.mixin.android.extension.defaultSharedPreferences
import one.mixin.android.extension.getStringDeviceId
import one.mixin.android.extension.putString
import one.mixin.android.util.analytics.AnalyticsTracker
import one.mixin.android.util.database.clearJobsAndRawTransaction
import one.mixin.android.vo.Account

Expand All @@ -31,6 +32,7 @@ suspend fun initializeAccountSession(
Session.storeEd25519Seed(privateKey.base64Encode())
Session.storePinToken(pinToken.base64Encode())
Session.storeAccount(account)
AnalyticsTracker.setAppsFlyerCustomerUserId(account)

// Enter the user scope and migrate databases BEFORE clearing anything
// This ensures we operate on the correct scoped database after migration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,9 @@ class MnemonicPhraseFragment : BaseFragment(R.layout.fragment_compose) {
if (r?.isSuccess == true) {
val account = r.data!!
initializeAccountSession(requireContext(), account, sessionKey)
if (words.isNullOrEmpty()) {
AnalyticsTracker.trackSignUpAccountCreated(account)
}
when {
account.fullName.isNullOrBlank() -> {
withContext(Dispatchers.IO) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import one.mixin.android.ui.common.BaseFragment
import one.mixin.android.ui.common.recyclerview.HeaderAdapter
import one.mixin.android.ui.wallet.adapter.AssetItemCallback
import one.mixin.android.ui.wallet.adapter.WalletAssetAdapter
import one.mixin.android.util.analytics.AnalyticsTracker
import one.mixin.android.util.analytics.AnalyticsTracker.TradeWallet
import one.mixin.android.util.viewBinding
import one.mixin.android.vo.safe.TokenItem
import kotlin.math.abs
Expand Down Expand Up @@ -66,6 +68,7 @@ class HiddenAssetsFragment : BaseFragment(R.layout.fragment_hidden_assets), Head
val asset = assetsAdapter.data!![assetsAdapter.getPosition(hiddenPos)]
val deleteItem = assetsAdapter.removeItem(hiddenPos)!!
lifecycleScope.launch {
AnalyticsTracker.trackAssetVisibility(false, TradeWallet.MAIN, AnalyticsTracker.AssetSource.WALLET_HOME)
walletViewModel.updateAssetHidden(asset.assetId, false)
val anchorView = assetsRv

Expand All @@ -74,6 +77,7 @@ class HiddenAssetsFragment : BaseFragment(R.layout.fragment_hidden_assets), Head
.setAction(R.string.UNDO) {
assetsAdapter.restoreItem(deleteItem, hiddenPos)
lifecycleScope.launch(Dispatchers.IO) {
AnalyticsTracker.trackAssetVisibility(true, TradeWallet.MAIN, AnalyticsTracker.AssetSource.WALLET_HOME)
walletViewModel.updateAssetHidden(asset.assetId, true)
}
}.setActionTextColor(ContextCompat.getColor(requireContext(), R.color.wallet_blue)).apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,22 @@ class MarketShareBottomFragment : MixinComposeBottomSheetDialogFragment() {
close.setOnClickListener { dismiss() }
share.setOnClickListener {
if (isLoading) return@setOnClickListener
AnalyticsTracker.trackMarketDetailShare(AnalyticsTracker.MarketShareType.SHARE_IMAGE)
AnalyticsTracker.trackShareMarket(AnalyticsTracker.MarketShareType.SHARE_IMAGE)
shareToSystem()
}
mixinContact.setOnClickListener {
if (isLoading) return@setOnClickListener
AnalyticsTracker.trackShareMarket(AnalyticsTracker.MarketShareType.MIXIN_CONTACT)
shareToMixinContact()
}
copy.setOnClickListener {
if (isLoading) return@setOnClickListener
AnalyticsTracker.trackMarketDetailShare(AnalyticsTracker.MarketShareType.COPY_LINK)
AnalyticsTracker.trackShareMarket(AnalyticsTracker.MarketShareType.COPY_LINK)
copyLink()
}
save.setOnClickListener {
if (isLoading) return@setOnClickListener
AnalyticsTracker.trackMarketDetailShare(AnalyticsTracker.MarketShareType.SAVE_TO_ALBUM)
AnalyticsTracker.trackShareMarket(AnalyticsTracker.MarketShareType.SAVE_TO_ALBUM)
saveToAlbum()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,9 +349,10 @@ class TransactionsFragment : BaseFragment(R.layout.fragment_transactions), OnSna
bottomBinding.apply {
hide.setText(if (asset.hidden == true) R.string.Show else R.string.Hide)
hide.setOnClickListener {
AnalyticsTracker.trackAssetDetailHide()
val hidden = asset.hidden != true
AnalyticsTracker.trackAssetVisibility(hidden, TradeWallet.MAIN, AnalyticsTracker.AssetSource.ASSET_DETAIL)
lifecycleScope.launch(Dispatchers.IO) {
walletViewModel.updateAssetHidden(asset.assetId, asset.hidden != true)
walletViewModel.updateAssetHidden(asset.assetId, hidden)
}
bottomSheet.dismiss()
mainThreadDelayed({ activity?.onBackPressedDispatcher?.onBackPressed() }, 200)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import one.mixin.android.ui.common.recyclerview.HeaderAdapter
import one.mixin.android.ui.home.web3.Web3ViewModel
import one.mixin.android.ui.wallet.adapter.AssetItemCallback
import one.mixin.android.ui.wallet.adapter.WalletWeb3TokenAdapter
import one.mixin.android.util.analytics.AnalyticsTracker
import one.mixin.android.util.analytics.AnalyticsTracker.TradeWallet
import one.mixin.android.util.viewBinding
import one.mixin.android.web3.details.Web3TransactionsFragment
import kotlin.math.abs
Expand Down Expand Up @@ -81,6 +83,7 @@ class Web3HiddenAssetsFragment : BaseFragment(R.layout.fragment_hidden_assets),
val asset = assetsAdapter.data!![assetsAdapter.getPosition(hiddenPos)]
val deleteItem = assetsAdapter.removeItem(hiddenPos)!!
lifecycleScope.launch {
AnalyticsTracker.trackAssetVisibility(false, TradeWallet.WEB3, AnalyticsTracker.AssetSource.WALLET_HOME)
web3ViewModel.updateTokenHidden(asset.assetId, asset.walletId, false)
val anchorView = assetsRv

Expand All @@ -89,6 +92,7 @@ class Web3HiddenAssetsFragment : BaseFragment(R.layout.fragment_hidden_assets),
.setAction(R.string.UNDO) {
assetsAdapter.restoreItem(deleteItem, hiddenPos)
lifecycleScope.launch {
AnalyticsTracker.trackAssetVisibility(true, TradeWallet.WEB3, AnalyticsTracker.AssetSource.WALLET_HOME)
web3ViewModel.updateTokenHidden(asset.assetId, asset.walletId, true)
}
}.setActionTextColor(ContextCompat.getColor(requireContext(), R.color.wallet_blue)).apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package one.mixin.android.util.analytics

import java.math.BigDecimal

internal data class AnalyticsEvent(
val name: String,
val params: Map<String, String> = emptyMap(),
)

internal object AnalyticsRules {
private const val NON_ORGANIC = "Non-Organic"

private val directAppsFlyerEvents = setOf(
"sign_up_start",
"sign_up_account_created",
"login_start",
"buy_start",
"asset_receive_start",
"asset_receive_end",
"asset_receive_success",
"trade_spot_start",
"trade_spot_end",
"trade_perps_open_position_start",
"trade_perps_open_position_end",
"asset_send_start",
"asset_send_end",
"share_market",
"hide_asset",
"show_asset",
)

fun appsFlyerEventName(eventName: String): String? =
when (eventName) {
"login_end" -> "af_login"
"sign_up_end" -> "af_complete_registration"
in directAppsFlyerEvents -> eventName
else -> null
}

fun conversionUserProperties(conversionData: Map<String, Any?>): Map<String, String> {
val status = conversionData["af_status"]?.toString()?.takeIf { it.isNotBlank() } ?: return emptyMap()
val properties = linkedMapOf("af_source" to status)
if (status == NON_ORGANIC) {
conversionData["media_source"]?.toString()?.takeIf { it.isNotBlank() }?.let {
properties["af_media_source"] = it
}
conversionData["campaign"]?.toString()?.takeIf { it.isNotBlank() }?.let {
properties["af_campaign"] = it
}
}
return properties
}

fun marketShareEvent(type: String) =
AnalyticsEvent("share_market", mapOf("type" to type))

fun spotOrdersEvent(type: String) =
AnalyticsEvent("trade_spot_orders", mapOf("type" to type))

fun spotOrderDetailEvent(type: String) =
AnalyticsEvent("trade_spot_order_detail", mapOf("type" to type))

fun assetVisibilityEvent(
hidden: Boolean,
wallet: String,
source: String,
) =
if (hidden) {
AnalyticsEvent(
"hide_asset",
mapOf(
"wallet" to wallet,
"source" to source,
),
)
} else {
AnalyticsEvent("show_asset", mapOf("wallet" to wallet))
}

fun receiveAssetLevel(amountUsd: BigDecimal): String =
when {
amountUsd >= BigDecimal("1000000") -> "v1,000,000"
amountUsd >= BigDecimal("100000") -> "v100,000"
amountUsd >= BigDecimal("10000") -> "v10,000"
amountUsd >= BigDecimal("1000") -> "v1,000"
amountUsd >= BigDecimal("100") -> "v100"
amountUsd > BigDecimal.ZERO -> "v1"
else -> "v0"
}
}
Loading