Skip to content

Commit 2d59a5e

Browse files
authored
chore: allow screens to track own screen views on composition (#8)
Signed-off-by: Brandon McAnsh <[email protected]>
1 parent a52ebb1 commit 2d59a5e

File tree

20 files changed

+323
-146
lines changed

20 files changed

+323
-146
lines changed

api/src/main/java/com/getcode/manager/AnalyticsManager.kt

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,40 @@ import timber.log.Timber
1414
import javax.inject.Inject
1515
import javax.inject.Singleton
1616

17+
interface AnalyticsService {
18+
fun open(screen: AnalyticsManager.Screen)
19+
fun login(ownerPublicKey: String, autoCompleteCount: Int, inputChangeCount: Int)
20+
fun logout()
21+
fun track(event: AnalyticsManager.Name, vararg properties: Pair<AnalyticsManager.Property, String>)
22+
}
23+
24+
class AnalyticsServiceNull : AnalyticsService {
25+
override fun open(screen: AnalyticsManager.Screen) = Unit
26+
27+
override fun login(ownerPublicKey: String, autoCompleteCount: Int, inputChangeCount: Int) = Unit
28+
29+
override fun logout() = Unit
30+
31+
override fun track(
32+
event: AnalyticsManager.Name,
33+
vararg properties: Pair<AnalyticsManager.Property, String>
34+
) = Unit
35+
}
36+
1737
@Singleton
18-
class AnalyticsManager @Inject constructor(private val mixpanelAPI: MixpanelAPI) {
38+
class AnalyticsManager @Inject constructor(private val mixpanelAPI: MixpanelAPI): AnalyticsService {
1939
private var grabStartMillis: Long = 0L
2040
private var cashLinkGrabStartMillis: Long = 0L
2141

22-
fun open(screen: Screen) {
42+
override fun open(screen: Screen) {
2343
track(Name.Open, Pair(Property.Screen, screen.value))
2444
}
2545

26-
fun logout() {
46+
override fun logout() {
2747
track(Name.Logout)
2848
}
2949

30-
fun login(ownerPublicKey: String, autoCompleteCount: Int, inputChangeCount: Int) {
50+
override fun login(ownerPublicKey: String, autoCompleteCount: Int, inputChangeCount: Int) {
3151
track(
3252
Name.Login,
3353
Pair(Property.OwnerPublicKey, ownerPublicKey),
@@ -151,8 +171,11 @@ class AnalyticsManager @Inject constructor(private val mixpanelAPI: MixpanelAPI)
151171
Timber.i("Bill scanned. From start: " + (System.currentTimeMillis() - (timeAppInit ?: 0)))
152172
}
153173

154-
private fun track(event: Name, vararg properties: Pair<Property, String>) {
155-
if (BuildConfig.DEBUG) return //no logging in debug
174+
override fun track(event: Name, vararg properties: Pair<Property, String>) {
175+
if (BuildConfig.DEBUG) {
176+
Timber.d("debug track $event, ${properties.map { "${it.first.name}, ${it.second}" }}")
177+
return
178+
} //no logging in debug
156179

157180
val jsonObject = JSONObject()
158181
properties.forEach { property ->
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.getcode
2+
3+
import androidx.compose.runtime.ProvidableCompositionLocal
4+
import androidx.compose.runtime.staticCompositionLocalOf
5+
import com.getcode.manager.AnalyticsService
6+
import com.getcode.manager.AnalyticsServiceNull
7+
8+
val LocalAnalytics: ProvidableCompositionLocal<AnalyticsService> = staticCompositionLocalOf { AnalyticsServiceNull() }
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.getcode.analytics
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.lifecycle.LifecycleOwner
5+
import com.getcode.manager.AnalyticsManager
6+
7+
@Composable
8+
fun AnalyticsScreenWatcher(
9+
lifecycleOwner: LifecycleOwner,
10+
event: AnalyticsManager.Screen,
11+
) {
12+
AnalyticsWatcher(lifecycleOwner = lifecycleOwner, onEvent = { analytics, context ->
13+
analytics.open(event)
14+
})
15+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.getcode.analytics
2+
3+
import android.content.Context
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.runtime.getValue
6+
import androidx.compose.runtime.rememberUpdatedState
7+
import androidx.compose.ui.platform.LocalContext
8+
import androidx.lifecycle.Lifecycle
9+
import androidx.lifecycle.LifecycleOwner
10+
import com.getcode.LocalAnalytics
11+
import com.getcode.manager.AnalyticsService
12+
import com.getcode.util.RepeatOnLifecycle
13+
14+
@Composable
15+
fun AnalyticsWatcher(
16+
lifecycleOwner: LifecycleOwner,
17+
onEvent: (AnalyticsService, Context) -> Unit,
18+
onDispose: (AnalyticsService, Context) -> Unit = { _, _ -> },
19+
) {
20+
val context = LocalContext.current
21+
val analyticsService = LocalAnalytics.current
22+
23+
val updatedOnEvent by rememberUpdatedState(newValue = onEvent)
24+
val updatedOnDispose by rememberUpdatedState(newValue = onDispose)
25+
26+
RepeatOnLifecycle(
27+
lifecycleOwner = lifecycleOwner,
28+
targetState = Lifecycle.State.RESUMED,
29+
doOnDispose = {
30+
updatedOnDispose(analyticsService, context)
31+
},
32+
) {
33+
updatedOnEvent(analyticsService, context)
34+
}
35+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.getcode.util
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.DisposableEffect
5+
import androidx.compose.ui.platform.LocalLifecycleOwner
6+
import androidx.lifecycle.Lifecycle
7+
import androidx.lifecycle.LifecycleOwner
8+
import androidx.lifecycle.lifecycleScope
9+
import androidx.lifecycle.repeatOnLifecycle
10+
import kotlinx.coroutines.launch
11+
12+
@Composable
13+
fun RepeatOnLifecycle(
14+
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
15+
targetState: Lifecycle.State,
16+
doOnDispose: () -> Unit = {},
17+
action: suspend () -> Unit,
18+
) {
19+
DisposableEffect(lifecycleOwner) {
20+
val job = lifecycleOwner.lifecycleScope.launch {
21+
lifecycleOwner.repeatOnLifecycle(targetState) {
22+
action()
23+
}
24+
}
25+
onDispose {
26+
job.cancel()
27+
doOnDispose()
28+
}
29+
}
30+
}

app/src/main/java/com/getcode/view/MainActivity.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.os.Bundle
66
import android.os.Debug
77
import androidx.activity.ComponentActivity
88
import androidx.activity.compose.setContent
9+
import androidx.compose.runtime.CompositionLocalProvider
910
import androidx.compose.runtime.DisposableEffect
1011
import androidx.compose.runtime.rememberUpdatedState
1112
import androidx.core.util.Consumer
@@ -15,6 +16,7 @@ import androidx.navigation.NavController
1516
import androidx.navigation.NavHostController
1617
import androidx.navigation.findNavController
1718
import com.getcode.CodeApp
19+
import com.getcode.LocalAnalytics
1820
import com.getcode.manager.AnalyticsManager
1921
import com.getcode.manager.AuthManager
2022
import com.getcode.manager.SessionManager
@@ -78,7 +80,9 @@ class MainActivity : FragmentActivity() {
7880
setContent {
7981
val appState = rememberCodeAppState()
8082
currentNavHostController = appState.navController
81-
CodeApp(appState)
83+
CompositionLocalProvider(LocalAnalytics provides analyticsManager) {
84+
CodeApp(appState)
85+
}
8286
}
8387
}
8488

app/src/main/java/com/getcode/view/main/account/AccountAccessKey.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier
1313
import androidx.compose.ui.graphics.asImageBitmap
1414
import androidx.compose.ui.layout.ContentScale
1515
import androidx.compose.ui.platform.LocalContext
16+
import androidx.compose.ui.platform.LocalLifecycleOwner
1617
import androidx.compose.ui.res.stringResource
1718
import androidx.compose.ui.text.style.TextAlign
1819
import androidx.compose.ui.unit.dp
@@ -22,6 +23,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
2223
import androidx.navigation.NavController
2324
import com.getcode.App
2425
import com.getcode.R
26+
import com.getcode.analytics.AnalyticsScreenWatcher
27+
import com.getcode.manager.AnalyticsManager
2528
import com.getcode.manager.TopBarManager
2629
import com.getcode.theme.BrandLight
2730
import com.getcode.util.IntentUtils
@@ -76,6 +79,11 @@ fun AccountAccessKey(navController: NavController) {
7679
}
7780
}
7881

82+
AnalyticsScreenWatcher(
83+
lifecycleOwner = LocalLifecycleOwner.current,
84+
event = AnalyticsManager.Screen.Backup
85+
)
86+
7987
ConstraintLayout(
8088
modifier = Modifier
8189
.fillMaxSize()

app/src/main/java/com/getcode/view/main/account/AccountDeposit.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import androidx.compose.ui.Alignment
1010
import androidx.compose.ui.Modifier
1111
import androidx.compose.ui.draw.clip
1212
import androidx.compose.ui.platform.LocalClipboardManager
13+
import androidx.compose.ui.platform.LocalLifecycleOwner
1314
import androidx.compose.ui.res.stringResource
1415
import androidx.compose.ui.text.AnnotatedString
1516
import androidx.compose.ui.text.style.TextAlign
1617
import androidx.compose.ui.unit.dp
1718
import com.getcode.R
19+
import com.getcode.analytics.AnalyticsScreenWatcher
20+
import com.getcode.manager.AnalyticsManager
1821
import com.getcode.manager.SessionManager
1922
import com.getcode.solana.organizer.AccountType
2023
import com.getcode.theme.*
@@ -32,6 +35,11 @@ fun AccountDeposit() {
3235
val localClipboardManager = LocalClipboardManager.current
3336
var isCopied by remember { mutableStateOf(false) }
3437

38+
AnalyticsScreenWatcher(
39+
lifecycleOwner = LocalLifecycleOwner.current,
40+
event = AnalyticsManager.Screen.Deposit
41+
)
42+
3543
Column(
3644
modifier = Modifier
3745
.background(Brand)

app/src/main/java/com/getcode/view/main/account/AccountDetails.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ fun AccountDetails(
2222
viewModel: AccountSheetViewModel = hiltViewModel(),
2323
) {
2424
val dataState by viewModel.stateFlow.collectAsState()
25-
val context = LocalContext.current
26-
27-
fun onPage(page: AccountPage) {
28-
onPageSelected(page)
29-
viewModel.dispatchEvent(AccountSheetViewModel.Event.Navigate(page))
30-
}
3125

3226
Box(modifier = Modifier.fillMaxHeight()) {
3327
Column(
@@ -48,7 +42,7 @@ fun AccountDetails(
4842
positiveText = App.getInstance()
4943
.getString(R.string.action_viewAccessKey),
5044
negativeText = App.getInstance().getString(R.string.action_cancel),
51-
onPositive = { onPage(AccountPage.ACCESS_KEY) },
45+
onPositive = { onPageSelected(AccountPage.ACCESS_KEY) },
5246
onNegative = {}
5347
)
5448
)
@@ -58,11 +52,11 @@ fun AccountDetails(
5852
name = R.string.title_phoneNumber,
5953
icon = R.drawable.ic_menu_phone,
6054
isPhoneLinked = dataState.isPhoneLinked,
61-
) { onPage(AccountPage.PHONE) },
55+
) { onPageSelected(AccountPage.PHONE) },
6256
AccountMainItem(
6357
name = R.string.action_deleteAccount,
6458
icon = R.drawable.ic_delete
65-
) { onPage(AccountPage.DELETE_ACCOUNT) },
59+
) { onPageSelected(AccountPage.DELETE_ACCOUNT) },
6660
)
6761

6862
for (action in actions) {

app/src/main/java/com/getcode/view/main/account/AccountFaq.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@ import androidx.compose.runtime.LaunchedEffect
1010
import androidx.compose.runtime.collectAsState
1111
import androidx.compose.runtime.getValue
1212
import androidx.compose.ui.Modifier
13+
import androidx.compose.ui.platform.LocalLifecycleOwner
1314
import androidx.compose.ui.text.font.FontWeight
1415
import androidx.compose.ui.tooling.preview.Preview
1516
import androidx.compose.ui.unit.dp
1617
import androidx.compose.ui.unit.sp
1718
import androidx.hilt.navigation.compose.hiltViewModel
1819
import com.getcode.R
20+
import com.getcode.analytics.AnalyticsScreenWatcher
21+
import com.getcode.manager.AnalyticsManager
1922
import com.getcode.theme.White
2023
import com.getcode.theme.sheetHeight
2124
import com.getcode.view.components.MarkdownText
@@ -27,6 +30,11 @@ fun AccountFaq(
2730
) {
2831
val dataState by viewModel.stateFlow.collectAsState()
2932

33+
AnalyticsScreenWatcher(
34+
lifecycleOwner = LocalLifecycleOwner.current,
35+
event = AnalyticsManager.Screen.Faq
36+
)
37+
3038
Box(
3139
modifier = Modifier
3240
.padding(horizontal = 20.dp)

app/src/main/java/com/getcode/view/main/account/AccountHome.kt

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ fun AccountHome(
5757
val dataState by viewModel.stateFlow.collectAsState()
5858
val context = LocalContext.current
5959

60-
fun onPage(page: AccountPage) {
61-
onPageSelected(page)
62-
viewModel.dispatchEvent(AccountSheetViewModel.Event.Navigate(page))
63-
}
64-
6560
Box(modifier = Modifier.fillMaxHeight()) {
6661
Column(
6762
modifier = Modifier
@@ -71,23 +66,23 @@ fun AccountHome(
7166
AccountMainItem(
7267
name = R.string.title_buyAndSellKin,
7368
icon = R.drawable.ic_currency_dollar_active
74-
) { onPage(AccountPage.BUY_AND_SELL_KIN) },
69+
) { onPageSelected(AccountPage.BUY_AND_SELL_KIN) },
7570
AccountMainItem(
7671
name = R.string.title_depositKin,
7772
icon = R.drawable.ic_menu_deposit
78-
) { onPage(AccountPage.DEPOSIT) },
73+
) { onPageSelected(AccountPage.DEPOSIT) },
7974
AccountMainItem(
8075
name = R.string.title_withdrawKin,
8176
icon = R.drawable.ic_menu_withdraw
82-
) { onPage(AccountPage.WITHDRAW) },
77+
) { onPageSelected(AccountPage.WITHDRAW) },
8378
AccountMainItem(
8479
name = R.string.title_myAccount,
8580
icon = R.drawable.ic_menu_account
86-
) { onPage(AccountPage.ACCOUNT_DETAILS) },
81+
) { onPageSelected(AccountPage.ACCOUNT_DETAILS) },
8782
AccountMainItem(
8883
name = R.string.title_faq,
8984
icon = R.drawable.ic_faq,
90-
) { onPage(AccountPage.FAQ) },
85+
) { onPageSelected(AccountPage.FAQ) },
9186
AccountMainItem(
9287
name = R.string.action_logout,
9388
icon = R.drawable.ic_menu_logout
@@ -115,7 +110,7 @@ fun AccountHome(
115110
AccountMainItem(
116111
name = R.string.account_debug_options,
117112
icon = R.drawable.ic_bug,
118-
) { onPage(AccountPage.ACCOUNT_DEBUG_OPTIONS) }
113+
) { onPageSelected(AccountPage.ACCOUNT_DEBUG_OPTIONS) }
119114
.let { actions.add(4, it) }
120115
}
121116

app/src/main/java/com/getcode/view/main/account/AccountPhone.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import androidx.compose.foundation.layout.padding
77
import androidx.compose.foundation.layout.size
88
import androidx.compose.material.MaterialTheme
99
import androidx.compose.material.Text
10-
import androidx.compose.runtime.*
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.runtime.SideEffect
12+
import androidx.compose.runtime.collectAsState
13+
import androidx.compose.runtime.getValue
1114
import androidx.compose.ui.Modifier
1215
import androidx.compose.ui.res.painterResource
1316
import androidx.compose.ui.res.stringResource
@@ -16,14 +19,16 @@ import androidx.constraintlayout.compose.ConstraintLayout
1619
import androidx.hilt.navigation.compose.hiltViewModel
1720
import androidx.navigation.NavController
1821
import com.getcode.App
22+
import com.getcode.R
1923
import com.getcode.manager.BottomBarManager
2024
import com.getcode.manager.SessionManager
2125
import com.getcode.network.repository.urlEncode
2226
import com.getcode.theme.Brand
23-
import com.getcode.view.*
27+
import com.getcode.view.ARG_IS_PHONE_LINKING
28+
import com.getcode.view.ARG_SIGN_IN_ENTROPY_B64
29+
import com.getcode.view.LoginSections
2430
import com.getcode.view.components.ButtonState
2531
import com.getcode.view.components.CodeButton
26-
import com.getcode.R
2732

2833
@Composable
2934
fun AccountPhone(navController: NavController? = null) {

0 commit comments

Comments
 (0)