mirror of
https://github.com/kickstarter/android-oss.git
synced 2026-03-13 09:11:01 +08:00
MBL-2424 Onboarding analytics (#2393)
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user