MBL-2424 Onboarding analytics (#2393)

This commit is contained in:
Yun Cheng
2025-07-30 10:37:00 -04:00
committed by GitHub
parent 5febe72319
commit d1ca58cd82
8 changed files with 211 additions and 48 deletions

View File

@@ -1,5 +1,6 @@
package com.kickstarter.libs
import OnboardingPage
import com.kickstarter.features.pledgedprojectsoverview.data.PPOCard
import com.kickstarter.libs.utils.AnalyticEventsUtils
import com.kickstarter.libs.utils.ContextPropertyKeyName.COMMENT_BODY
@@ -20,6 +21,7 @@ import com.kickstarter.libs.utils.EventContextValues.ContextPageName.CHECKOUT
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.LOGIN
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.LOGIN_SIGN_UP
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.MANAGE_PLEDGE
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.ONBOARDING
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.PROJECT
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.PROJECT_ALERTS
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.REWARDS
@@ -27,6 +29,8 @@ import com.kickstarter.libs.utils.EventContextValues.ContextPageName.SIGN_UP
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.THANKS
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.TWO_FACTOR_AUTH
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.UPDATE_PLEDGE
import com.kickstarter.libs.utils.EventContextValues.ContextSectionName.ACTIVITY_TRACKING_PROMPT
import com.kickstarter.libs.utils.EventContextValues.ContextSectionName.ENABLE_NOTIFICATIONS_PROMPT
import com.kickstarter.libs.utils.EventContextValues.ContextTypeName.ADDRESS
import com.kickstarter.libs.utils.EventContextValues.ContextTypeName.CREDIT_CARD
import com.kickstarter.libs.utils.EventContextValues.ContextTypeName.REPLY
@@ -34,7 +38,9 @@ import com.kickstarter.libs.utils.EventContextValues.ContextTypeName.ROOT
import com.kickstarter.libs.utils.EventContextValues.ContextTypeName.UNWATCH
import com.kickstarter.libs.utils.EventContextValues.ContextTypeName.WATCH
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.ADD_ONS_CONTINUE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.ALLOW_TRACKING
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.CAMPAIGN_DETAILS
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.CLOSE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.COMMENT_POST
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.CONFIRM_INITIATE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.CONFIRM_SUBMIT
@@ -44,16 +50,19 @@ import com.kickstarter.libs.utils.EventContextValues.CtaContextName.DISCOVER_SOR
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.EDIT
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.FINALIZE_PLEDGE_INITIATE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.FIX_PLEDGE_INITIATE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.GET_NOTIFIED
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.LATE_PLEDGE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.LOGIN_INITIATE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.LOGIN_OR_SIGN_UP
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.LOGIN_SUBMIT
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.MESSAGE_CREATOR_INITIATE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.NEXT
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.PLEDGE_CONFIRM
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.PLEDGE_INITIATE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.PLEDGE_SUBMIT
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.REWARD_CONTINUE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.SEARCH
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.SIGNUP_LOGIN
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.SIGN_UP_INITIATE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.SIGN_UP_SUBMIT
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.SURVEY_RESPONSE_INITIATE
@@ -87,7 +96,6 @@ import com.kickstarter.ui.data.CheckoutData
import com.kickstarter.ui.data.PledgeData
import com.kickstarter.ui.data.ProjectData
import java.util.Locale
import kotlin.collections.HashMap
class AnalyticEvents(trackingClients: List<TrackingClientType?>) {
@@ -842,6 +850,75 @@ class AnalyticEvents(trackingClients: List<TrackingClientType?>) {
}
}
/**
* Sends data to the client when onboarding flow screens are viewed
*/
fun trackOnboardingPageViewed(onboardingPage: String) {
val props: HashMap<String, Any> = hashMapOf(CONTEXT_PAGE.contextName to ONBOARDING.contextName)
props[CONTEXT_SECTION.contextName] = onboardingPage
client.track(PAGE_VIEWED.eventName, props)
}
/**
* Sends data to the client when the close CTA is tapped on the onboarding flow
*/
fun trackOnboardingCloseCTAClicked(onboardingPage: String) {
val props: HashMap<String, Any> = hashMapOf(CONTEXT_PAGE.contextName to ONBOARDING.contextName)
props[CONTEXT_SECTION.contextName] = onboardingPage
props[CONTEXT_CTA.contextName] = CLOSE.contextName
client.track(CTA_CLICKED.eventName, props)
}
/**
* Sends data to the client when the next CTA is tapped on the onboarding flow
*/
fun trackOnboardingNextCTAClicked(onboardingPage: String) {
val props: HashMap<String, Any> = hashMapOf(CONTEXT_PAGE.contextName to ONBOARDING.contextName)
props[CONTEXT_SECTION.contextName] = onboardingPage
props[CONTEXT_CTA.contextName] = NEXT.contextName
client.track(CTA_CLICKED.eventName, props)
}
/**
* Sends data to the client when the Get Notified CTA is tapped on the onboarding flow
*/
fun trackOnboardingGetNotifiedCTAClicked() {
val props: HashMap<String, Any> = hashMapOf(CONTEXT_PAGE.contextName to ONBOARDING.contextName)
props[CONTEXT_SECTION.contextName] = ENABLE_NOTIFICATIONS_PROMPT.contextName
props[CONTEXT_CTA.contextName] = GET_NOTIFIED.contextName
client.track(CTA_CLICKED.eventName, props)
}
/**
* Sends data to the client when the Allow Tracking CTA is tapped on the onboarding flow
*/
fun trackOnboardingAllowTrackingCTAClicked() {
val props: HashMap<String, Any> = hashMapOf(CONTEXT_PAGE.contextName to ONBOARDING.contextName)
props[CONTEXT_SECTION.contextName] = ACTIVITY_TRACKING_PROMPT.contextName
props[CONTEXT_CTA.contextName] = ALLOW_TRACKING.contextName
client.track(CTA_CLICKED.eventName, props)
}
/**
* Sends data to the client when the either the allow or deny CTA is tapped on the dialog prompt
*/
fun trackOnboardingAllowDenyPromptCTAClicked(prompt: String, allowOrDeny: String) {
val props: HashMap<String, Any> = hashMapOf(CONTEXT_PAGE.contextName to ONBOARDING.contextName)
props[CONTEXT_SECTION.contextName] = prompt
props[CONTEXT_CTA.contextName] = allowOrDeny
client.track(CTA_CLICKED.eventName, props)
}
/**
* Sends data to the client when the close CTA is tapped on the onboarding flow
*/
fun trackOnboardingSignupLoginCTAClicked() {
val props: HashMap<String, Any> = hashMapOf(CONTEXT_PAGE.contextName to ONBOARDING.contextName)
props[CONTEXT_SECTION.contextName] = OnboardingPage.SIGNUP_LOGIN
props[CONTEXT_CTA.contextName] = SIGNUP_LOGIN.contextName
client.track(CTA_CLICKED.eventName, props)
}
fun reset() {
client.reset()
}

View File

@@ -48,10 +48,17 @@ class EventContextValues {
LOGIN_SUBMIT("log_in_submit"),
SIGN_UP_SUBMIT("sign_up_submit"),
SIGN_UP_INITIATE("sign_up_initiate"),
SIGNUP_LOGIN("signup_login"),
COMMENT_POST("comment_post"),
PROJECT_SELECT("project_select"),
LATE_PLEDGE("late_pledge"),
EDIT("edit"),
CLOSE("close"),
NEXT("next"),
GET_NOTIFIED("get_notified"),
ALLOW_TRACKING("allow_tracking"),
ALLOW("allow"),
DENY("deny"),
}
/**
@@ -91,7 +98,8 @@ class EventContextValues {
LOGIN("log_in"),
MANAGE_PLEDGE("manage_pledge"),
PROJECT_ALERTS("project_alerts"),
TWO_FACTOR_AUTH("two_factor_auth")
TWO_FACTOR_AUTH("two_factor_auth"),
ONBOARDING("onboarding"),
}
/**
@@ -115,6 +123,8 @@ class EventContextValues {
FAQS("faq"),
ENVIRONMENT("environment"),
AI("use_of_ai"),
ACTIVITY_TRACKING_PROMPT("activity_tracking_prompt"),
ENABLE_NOTIFICATIONS_PROMPT("enable_notifications_prompt"),
}
/**

View File

@@ -1,5 +1,6 @@
package com.kickstarter.ui.activities
import OnboardingPage
import OnboardingScreen
import android.Manifest
import android.content.Intent
@@ -12,6 +13,8 @@ import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.kickstarter.R
import com.kickstarter.libs.utils.ApplicationUtils
import com.kickstarter.libs.utils.EventContextValues.ContextSectionName.ACTIVITY_TRACKING_PROMPT
import com.kickstarter.libs.utils.EventContextValues.ContextSectionName.ENABLE_NOTIFICATIONS_PROMPT
import com.kickstarter.libs.utils.extensions.checkPermissions
import com.kickstarter.libs.utils.extensions.getEnvironment
import com.kickstarter.libs.utils.extensions.getLoginActivityIntent
@@ -38,10 +41,17 @@ class OnboardingFlowActivity : AppCompatActivity() {
isUserLoggedIn = viewModel.isUserLoggedIn(),
deviceNeedsNotificationPermissions = viewModel.deviceNeedsNotificationPermissions(),
onboardingCompleted = { onboardingCompleted() },
onboardingCancelled = { onboardingCancelled() },
turnOnNotifications = { permissionLauncher -> turnOnNotifications(permissionLauncher) },
allowTracking = { fragmentManager -> allowTracking(fragmentManager) },
signupOrLogin = { signupOrLogin() }
onboardingCancelled = { onboardingPage -> onboardingCancelled(onboardingPage) },
turnOnNotifications = { permissionLauncher ->
viewModel.trackOnboardingEnableNotificationsPromptCtaClicked()
turnOnNotifications(permissionLauncher)
},
allowTracking = { fragmentManager ->
viewModel.trackOnboardingAllowTrackingPromptCtaClicked()
allowTracking(fragmentManager)
},
signupOrLogin = { signupOrLogin() },
analyticEvents = viewModel.analytics()
)
}
}
@@ -52,12 +62,14 @@ class OnboardingFlowActivity : AppCompatActivity() {
ApplicationUtils.resumeDiscoveryActivity(this)
}
fun onboardingCancelled() {
fun onboardingCancelled(onboardingPage: OnboardingPage) {
viewModel.trackOnboardingCancelled(onboardingPage)
ApplicationUtils.resumeDiscoveryActivity(this)
}
fun turnOnNotifications(permissionLauncher: ActivityResultLauncher<String>) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && this.checkPermissions(Manifest.permission.POST_NOTIFICATIONS)) {
viewModel.trackOnboardingPromptViewed(ENABLE_NOTIFICATIONS_PROMPT.contextName)
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
// NOTE: we would not expect to reach this else block in the onboarding flow as we only show onboarding flow to new users
@@ -71,11 +83,13 @@ class OnboardingFlowActivity : AppCompatActivity() {
val consentManagementDialogFragment = ConsentManagementDialogFragment()
consentManagementDialogFragment.isCancelable = false
fragmentManager?.let {
viewModel.trackOnboardingPromptViewed(ACTIVITY_TRACKING_PROMPT.contextName)
consentManagementDialogFragment.show(it, "consentManagementDialogFragment")
}
}
fun signupOrLogin() {
viewModel.trackSignUpOrLoginCtaClicked()
val intent = Intent().getLoginActivityIntent(this, null, LoginReason.COMPLETED_ONBOARDING)
startActivityWithTransition(intent, R.anim.fade_in_slide_in_left, R.anim.slide_out_right)
finish()

View File

@@ -28,6 +28,7 @@ import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -50,6 +51,10 @@ import com.airbnb.lottie.compose.LottieConstants
import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
import com.kickstarter.R
import com.kickstarter.libs.AnalyticEvents
import com.kickstarter.libs.utils.EventContextValues.ContextSectionName.ACTIVITY_TRACKING_PROMPT
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.ALLOW
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.DENY
import com.kickstarter.ui.compose.designsystem.KSButton
import com.kickstarter.ui.compose.designsystem.KSButtonType
import com.kickstarter.ui.compose.designsystem.KSIconButton
@@ -59,12 +64,12 @@ import com.kickstarter.ui.compose.designsystem.KSTheme.dimensions
import com.kickstarter.ui.compose.designsystem.KSTheme.typographyV2
import com.kickstarter.ui.fragments.ConsentManagementDialogFragment
enum class OnboardingPage {
WELCOME,
SAVE_PROJECTS,
ENABLE_NOTIFICATIONS,
ACTIVITY_TRACKING,
LOGIN_SIGNUP,
enum class OnboardingPage(val analyticsSectionName: String) {
WELCOME("welcome"),
SAVE_PROJECTS("save_projects"),
ENABLE_NOTIFICATIONS("enable_notifications"),
ACTIVITY_TRACKING("activity_tracking"),
SIGNUP_LOGIN("signup_login"),
}
data class OnboardingPageData(
@@ -97,10 +102,11 @@ fun OnboardingScreen(
isUserLoggedIn: Boolean = false,
deviceNeedsNotificationPermissions: Boolean = false,
onboardingCompleted: () -> Unit = {},
onboardingCancelled: () -> Unit = {},
onboardingCancelled: (onboardingPage: OnboardingPage) -> Unit = {},
turnOnNotifications: (permissionLauncher: ActivityResultLauncher<String>) -> Unit = {},
allowTracking: (fragmentManager: FragmentManager?) -> Unit = {},
signupOrLogin: () -> Unit = {}
signupOrLogin: () -> Unit = {},
analyticEvents: AnalyticEvents? = null,
) {
val loggedOutPages = mutableListOf(
OnboardingPageData(
@@ -134,7 +140,7 @@ fun OnboardingScreen(
secondaryButtonText = stringResource(R.string.Not_right_now),
),
OnboardingPageData(
page = OnboardingPage.LOGIN_SIGNUP,
page = OnboardingPage.SIGNUP_LOGIN,
title = stringResource(R.string.onboarding_join_the_community_title),
description = stringResource(R.string.onboarding_join_the_community_subtitle),
animationRes = R.raw.android_onboarding_flow_login_signup,
@@ -147,6 +153,11 @@ fun OnboardingScreen(
val pages: List<OnboardingPageData> = if (isUserLoggedIn) loggedInPages.toList() else loggedOutPages.toList()
var currentPage by remember { mutableStateOf(0) }
LaunchedEffect(currentPage) {
// Launch Page Viewed analytics any time the currentPage changes
analyticEvents?.trackOnboardingPageViewed(pages[currentPage].page.analyticsSectionName)
}
val context = LocalContext.current
val activity = context as? ComponentActivity
@@ -155,10 +166,10 @@ fun OnboardingScreen(
contract = ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// Add analytics
analyticEvents?.trackOnboardingAllowDenyPromptCTAClicked(ACTIVITY_TRACKING_PROMPT.contextName, ALLOW.contextName)
currentPage++
} else {
// Add analytics
analyticEvents?.trackOnboardingAllowDenyPromptCTAClicked(ACTIVITY_TRACKING_PROMPT.contextName, DENY.contextName)
currentPage++
}
}
@@ -169,13 +180,12 @@ fun OnboardingScreen(
}
fragmentManager?.setFragmentResultListener(ConsentManagementDialogFragment.TAG, (context as FragmentActivity)) {
_, result ->
val value = result.getString("result")
val value = result.getString("result") ?: ""
analyticEvents?.trackOnboardingAllowDenyPromptCTAClicked(ACTIVITY_TRACKING_PROMPT.contextName, value)
if (isUserLoggedIn) { // Skip signup/login page
// Add analytics
onboardingCompleted()
} else {
// Add analytics
currentPage++
}
}
@@ -222,7 +232,7 @@ fun OnboardingScreen(
KSIconButton(
modifier = Modifier
.testTag(OnboardingScreenTestTags.CLOSE_BUTTON),
onClick = { onboardingCancelled() },
onClick = { onboardingCancelled(pages[currentPage].page) },
contentDescription = stringResource(R.string.Close),
imageVector = Icons.Filled.Close
)
@@ -245,13 +255,16 @@ fun OnboardingScreen(
text = pages[currentPage].buttonText,
onClickAction = {
when (pages[currentPage].page) {
OnboardingPage.WELCOME -> currentPage++
OnboardingPage.WELCOME -> {
analyticEvents?.trackOnboardingNextCTAClicked(pages[currentPage].page.analyticsSectionName)
currentPage++
}
OnboardingPage.SAVE_PROJECTS -> {
analyticEvents?.trackOnboardingNextCTAClicked(pages[currentPage].page.analyticsSectionName)
if (deviceNeedsNotificationPermissions) {
currentPage++ // Go to notifications page
} else {
currentPage++
currentPage++ // Skip notifications page and go to activity tracking
currentPage += 2 // Skip notifications page and go to activity tracking
}
}
OnboardingPage.ENABLE_NOTIFICATIONS -> {
@@ -262,7 +275,7 @@ fun OnboardingScreen(
activity?.let { allowTracking(fragmentManager) }
}
OnboardingPage.LOGIN_SIGNUP -> {
OnboardingPage.SIGNUP_LOGIN -> {
signupOrLogin()
}
}
@@ -277,6 +290,7 @@ fun OnboardingScreen(
KSButton(
text = secondaryText,
onClickAction = {
analyticEvents?.trackOnboardingNextCTAClicked(pages[currentPage].page.analyticsSectionName)
if (currentPage < pages.lastIndex) { // More pages remaining
if (pages[currentPage].page == OnboardingPage.ACTIVITY_TRACKING && isUserLoggedIn) {
// Skip signup/login page

View File

@@ -8,6 +8,8 @@ import androidx.fragment.app.DialogFragment
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.viewModels
import com.kickstarter.R
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.ALLOW
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.DENY
import com.kickstarter.libs.utils.extensions.getEnvironment
import com.kickstarter.viewmodels.ConsentManagementDialogFragmentViewModel
@@ -27,11 +29,11 @@ class ConsentManagementDialogFragment : DialogFragment() {
builder.setMessage(R.string.We_use_personal_data_to_provide_a_good_experience_on_Kickstarter_and_to_help_connect_you_with_projects_you_ll_love)
.setPositiveButton(R.string.Allow) { dialog, id ->
this.viewModel.inputs.userConsentPreference(true)
setFragmentResult(TAG, bundleOf("result" to "allow"))
setFragmentResult(TAG, bundleOf("result" to ALLOW.contextName))
}
.setNegativeButton(R.string.Decline) { dialog, id ->
this.viewModel.inputs.userConsentPreference(false)
setFragmentResult(TAG, bundleOf("result" to "decline"))
setFragmentResult(TAG, bundleOf("result" to DENY.contextName))
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")

View File

@@ -1,5 +1,6 @@
package com.kickstarter.viewmodels
import OnboardingPage
import android.os.Build
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@@ -18,15 +19,16 @@ class OnboardingFlowViewModel(
testDispatcher: CoroutineDispatcher? = null
) : ViewModel() {
private val currentUser = requireNotNull(environment.currentUserV2())
val sharedPreferences = requireNotNull(environment.sharedPreferences())
val ffClient = requireNotNull(environment.featureFlagClient())
private val sharedPreferences = requireNotNull(environment.sharedPreferences())
private val analytics = requireNotNull(environment.analytics())
private var isUserLoggedIn = false
private var deviceNeedsNotificationPermissions = false
private val scope = viewModelScope + (testDispatcher ?: EmptyCoroutineContext)
init {
deviceNeedsNotificationPermissions = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
deviceNeedsNotificationPermissions = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
!sharedPreferences.getBoolean(HAS_SEEN_NOTIF_PERMISSIONS, false)
scope.launch {
currentUser.observable().asFlow()
@@ -43,6 +45,22 @@ class OnboardingFlowViewModel(
sharedPreferences.edit().putBoolean(HAS_SEEN_NOTIF_PERMISSIONS, hasSeen).apply()
}
fun analytics() = analytics // Expose for Compose screen
fun trackSignUpOrLoginCtaClicked() {
analytics.trackOnboardingSignupLoginCTAClicked()
}
fun trackOnboardingPromptViewed(prompt: String) {
analytics.trackOnboardingPageViewed(prompt)
}
fun trackOnboardingAllowTrackingPromptCtaClicked() {
analytics.trackOnboardingAllowTrackingCTAClicked()
}
fun trackOnboardingEnableNotificationsPromptCtaClicked() {
analytics.trackOnboardingGetNotifiedCTAClicked()
}
fun trackOnboardingCancelled(onboardingPage: OnboardingPage) {
analytics.trackOnboardingCloseCTAClicked(onboardingPage.analyticsSectionName)
}
class Factory(
private val environment: Environment,
private val testDispatcher: CoroutineDispatcher? = null

View File

@@ -12,6 +12,7 @@ import com.kickstarter.libs.utils.ContextPropertyKeyName.COMMENT_CHARACTER_COUNT
import com.kickstarter.libs.utils.ContextPropertyKeyName.CONTEXT_CTA
import com.kickstarter.libs.utils.ContextPropertyKeyName.CONTEXT_LOCATION
import com.kickstarter.libs.utils.ContextPropertyKeyName.CONTEXT_PAGE
import com.kickstarter.libs.utils.ContextPropertyKeyName.CONTEXT_SECTION
import com.kickstarter.libs.utils.ContextPropertyKeyName.CONTEXT_TYPE
import com.kickstarter.libs.utils.ContextPropertyKeyName.PROJECT_UPDATE_ID
import com.kickstarter.libs.utils.EventContextValues
@@ -19,12 +20,14 @@ import com.kickstarter.libs.utils.EventContextValues.ContextPageName.ACTIVITY_FE
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.LOGIN
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.LOGIN_SIGN_UP
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.MANAGE_PLEDGE
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.ONBOARDING
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.PROJECT
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.PROJECT_ALERTS
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.SIGN_UP
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.THANKS
import com.kickstarter.libs.utils.EventContextValues.ContextPageName.TWO_FACTOR_AUTH
import com.kickstarter.libs.utils.EventContextValues.ContextTypeName.ADDRESS
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.CLOSE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.CONFIRM_INITIATE
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.CONFIRM_SUBMIT
import com.kickstarter.libs.utils.EventContextValues.CtaContextName.DISCOVER
@@ -1796,6 +1799,31 @@ class SegmentTest : KSRobolectricTestCase() {
assertEquals(11, expectedProperties["notification_count_total"])
}
@Test
fun `test onboarding page viewed`() {
val user = user()
val client = client(user)
client.eventNames.subscribe { this.segmentTrack.onNext(it) }.addToDisposable(disposables)
client.eventProperties.subscribe { this.propertiesTest.onNext(it) }.addToDisposable(disposables)
val segment = AnalyticEvents(listOf(client))
segment.trackOnboardingCloseCTAClicked(OnboardingPage.SAVE_PROJECTS.analyticsSectionName)
val properties = this.propertiesTest.value ?: mapOf()
assertContextProperties()
assertUserProperties(false)
assertSessionProperties(user)
this.segmentTrack.assertValue(CTA_CLICKED.eventName)
assertEquals(ONBOARDING.contextName, properties[CONTEXT_PAGE.contextName])
assertEquals(OnboardingPage.SAVE_PROJECTS.analyticsSectionName, properties[CONTEXT_SECTION.contextName])
assertEquals(CLOSE.contextName, properties[CONTEXT_CTA.contextName])
}
@Test
fun trackDiscoverFilterCTA_whenFilterPresent_returnsCorrectFilter() {
val user = user()

View File

@@ -871,18 +871,18 @@ class DiscoveryViewModelTest : KSRobolectricTestCase() {
Mockito.`when`(sharedPreferences.getBoolean(SharedPreferenceKey.HAS_SEEN_ONBOARDING, false)).thenReturn(false)
// New user heuristic: has not seen consent management or notification permissions
Mockito.`when`(sharedPreferences.contains(SharedPreferenceKey.CONSENT_MANAGEMENT_PREFERENCE)).thenReturn(false)
Mockito.`when`(sharedPreferences.contains(SharedPreferenceKey.CONSENT_MANAGEMENT_PREFERENCE)).thenReturn(true)
Mockito.`when`(sharedPreferences.getBoolean(SharedPreferenceKey.HAS_SEEN_NOTIF_PERMISSIONS, false)).thenReturn(false)
val mockFeatureFlagClient: MockFeatureFlagClient =
object : MockFeatureFlagClient() {
override fun getBoolean(FlagKey: FlagKey): Boolean {
if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_NATIVE_ONBOARDING_FLOW) {
return true
return if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_NATIVE_ONBOARDING_FLOW) {
true
} else if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_CONSENT_MANAGEMENT) {
return false
true
} else {
return false
false
}
}
}
@@ -901,23 +901,23 @@ class DiscoveryViewModelTest : KSRobolectricTestCase() {
}
@Test
fun testOnboardingFlow_hasSeenPreferenceFalseNewUserFalseAndFFOn_shouldEmit() {
fun testOnboardingFlow_hasSeenPreferenceFalseNewUserFalseAndFFOn_shouldNotEmit() {
val sharedPreferences: SharedPreferences = Mockito.mock(SharedPreferences::class.java)
Mockito.`when`(sharedPreferences.getBoolean(SharedPreferenceKey.HAS_SEEN_ONBOARDING, false)).thenReturn(false)
// New user heuristic: has not seen consent management or notification permissions
// NOT a new user, because user has seen both notification permissions and consent management
Mockito.`when`(sharedPreferences.contains(SharedPreferenceKey.CONSENT_MANAGEMENT_PREFERENCE)).thenReturn(true)
Mockito.`when`(sharedPreferences.getBoolean(SharedPreferenceKey.HAS_SEEN_NOTIF_PERMISSIONS, false)).thenReturn(true)
val mockFeatureFlagClient: MockFeatureFlagClient =
object : MockFeatureFlagClient() {
override fun getBoolean(FlagKey: FlagKey): Boolean {
if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_NATIVE_ONBOARDING_FLOW) {
return true
return if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_NATIVE_ONBOARDING_FLOW) {
true
} else if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_CONSENT_MANAGEMENT) {
return false
true
} else {
return false
false
}
}
}
@@ -940,10 +940,10 @@ class DiscoveryViewModelTest : KSRobolectricTestCase() {
val mockFeatureFlagClient: MockFeatureFlagClient =
object : MockFeatureFlagClient() {
override fun getBoolean(FlagKey: FlagKey): Boolean {
if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_NATIVE_ONBOARDING_FLOW) {
return false
return if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_NATIVE_ONBOARDING_FLOW) {
false
} else {
return false
false
}
}
}
@@ -968,10 +968,10 @@ class DiscoveryViewModelTest : KSRobolectricTestCase() {
val mockFeatureFlagClient: MockFeatureFlagClient =
object : MockFeatureFlagClient() {
override fun getBoolean(FlagKey: FlagKey): Boolean {
if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_NATIVE_ONBOARDING_FLOW) {
return true
return if (FlagKey == com.kickstarter.libs.featureflag.FlagKey.ANDROID_NATIVE_ONBOARDING_FLOW) {
true
} else {
return false
false
}
}
}