diff --git a/app/res/navigation/nav_graph_personalid_profile.xml b/app/res/navigation/nav_graph_personalid_profile.xml index b3f433dc93..b3fd1b9cd9 100644 --- a/app/res/navigation/nav_graph_personalid_profile.xml +++ b/app/res/navigation/nav_graph_personalid_profile.xml @@ -77,6 +77,9 @@ + @@ -140,6 +143,10 @@ + android:label="@string/personalid_set_new_backup_code_title"> + + diff --git a/app/src/org/commcare/fragments/personalId/BackupCodeWorkflow.kt b/app/src/org/commcare/fragments/personalId/BackupCodeWorkflow.kt index 362c8b681a..140b17f0aa 100644 --- a/app/src/org/commcare/fragments/personalId/BackupCodeWorkflow.kt +++ b/app/src/org/commcare/fragments/personalId/BackupCodeWorkflow.kt @@ -1,11 +1,16 @@ package org.commcare.fragments.personalId /** - * Identifies which backup-code flow is being executed. + * Why the user is on a backup-code screen. Passed as a nav argument so the confirm and + * set-new-code screens can report which journey they belong to, which their other arguments + * cannot tell them. * - * - [CONFIRM_BACKUP_CODE_CHANGE_CODE]: user is on the Manage Profile screen and wants to change - * their backup code; must confirm the current code first before setting a new one. + * - [CHANGE_BACKUP_CODE]: user chose to change their backup code from Manage Profile. + * - [EMAIL_CHANGE]: user is changing their email and must confirm the current code first. + * - [FORGOT_BACKUP_CODE]: user forgot their code and recovered via email OTP. */ enum class BackupCodeWorkflow { - CONFIRM_BACKUP_CODE_CHANGE_CODE, + CHANGE_BACKUP_CODE, + EMAIL_CHANGE, + FORGOT_BACKUP_CODE, } diff --git a/app/src/org/commcare/fragments/personalId/BasePersonalIdEmailVerificationFragment.kt b/app/src/org/commcare/fragments/personalId/BasePersonalIdEmailVerificationFragment.kt index d25c036f85..59c72bc882 100644 --- a/app/src/org/commcare/fragments/personalId/BasePersonalIdEmailVerificationFragment.kt +++ b/app/src/org/commcare/fragments/personalId/BasePersonalIdEmailVerificationFragment.kt @@ -19,6 +19,7 @@ import org.commcare.dalvik.databinding.FragmentPersonalidEmailVerificationBindin import org.commcare.fragments.extensions.hasLiveView import org.commcare.google.services.analytics.AnalyticsParamValue import org.commcare.google.services.analytics.FirebaseAnalyticsUtil +import org.commcare.utils.AccountSecurityAnalyticsMapper import org.commcare.utils.CommCareAttemptCounter import org.commcare.utils.OtpWaitFormatter import org.commcare.views.dialogs.StandardAlertDialog @@ -118,6 +119,13 @@ abstract class BasePersonalIdEmailVerificationFragment : BasePersonalIdFragment( activity = requireActivity() activity.setTitle(R.string.personalid_email_verification_appbar_title) activity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AccountSecurityAnalyticsMapper.workflowParam(workflow), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_VERIFY_EMAIL_OTP_SHOWN, + null, + null, + null, + ) binding.emailVerificationDescription.text = getString(R.string.personalid_email_verification_description, displayEmail()) @@ -256,6 +264,13 @@ abstract class BasePersonalIdEmailVerificationFragment : BasePersonalIdFragment( private fun onOtpLimitExceeded(throwable: Throwable?) { otpLimitExceeded = true + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AccountSecurityAnalyticsMapper.workflowParam(workflow), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_VERIFY_EMAIL_OTP_MAX_ATTEMPTS, + null, + null, + emailOtpTracker.failedAttempts, + ) binding.otpCodeView.clearCode() binding.otpCodeView.isEnabled = false enableVerifyButton(false) diff --git a/app/src/org/commcare/fragments/personalId/BasePersonalIdPhoneVerificationFragment.kt b/app/src/org/commcare/fragments/personalId/BasePersonalIdPhoneVerificationFragment.kt index a12ecaf82e..c861743fff 100644 --- a/app/src/org/commcare/fragments/personalId/BasePersonalIdPhoneVerificationFragment.kt +++ b/app/src/org/commcare/fragments/personalId/BasePersonalIdPhoneVerificationFragment.kt @@ -93,6 +93,8 @@ abstract class BasePersonalIdPhoneVerificationFragment : BasePersonalIdFragment( protected open fun onChangeNumberClicked() = Unit + protected open fun onOtpAttemptsExhausted() = Unit + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (savedInstanceState != null) { @@ -309,6 +311,7 @@ abstract class BasePersonalIdPhoneVerificationFragment : BasePersonalIdFragment( private fun onOtpLimitExceeded(throwable: Throwable?) { otpLimitExceeded = true + onOtpAttemptsExhausted() binding.customOtpView.clearCode() binding.customOtpView.isEnabled = false binding.connectPhoneVerifyButton.isEnabled = false diff --git a/app/src/org/commcare/fragments/personalId/BasePersonalIdSendEmailOtpFragment.kt b/app/src/org/commcare/fragments/personalId/BasePersonalIdSendEmailOtpFragment.kt index 3160c94483..4a122bfb8f 100644 --- a/app/src/org/commcare/fragments/personalId/BasePersonalIdSendEmailOtpFragment.kt +++ b/app/src/org/commcare/fragments/personalId/BasePersonalIdSendEmailOtpFragment.kt @@ -10,6 +10,9 @@ import org.commcare.dalvik.R import org.commcare.dalvik.databinding.FragmentPersonalidSendOtpBinding import org.commcare.fragments.extensions.hasLiveView import org.commcare.fragments.personalId.EmailHelper.maskEmail +import org.commcare.google.services.analytics.AnalyticsParamValue +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil +import org.commcare.utils.AccountSecurityAnalyticsMapper import org.commcare.utils.CommCareAttemptCounter /** @@ -48,6 +51,13 @@ abstract class BasePersonalIdSendEmailOtpFragment : BasePersonalIdFragment() { binding.sendOtpAddress.text = if (masked) maskEmail(email) else email binding.sendOtpButton.setOnClickListener { sendCode() } clearError() + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AccountSecurityAnalyticsMapper.workflowParam(workflow), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_SEND_EMAIL_OTP_SHOWN, + null, + null, + null, + ) } open fun setAppBarTitle() { diff --git a/app/src/org/commcare/fragments/personalId/PersonalIdAccountConfigSetNewBackupCodeFragment.kt b/app/src/org/commcare/fragments/personalId/PersonalIdAccountConfigSetNewBackupCodeFragment.kt index 750b4498d8..dc2391fa71 100644 --- a/app/src/org/commcare/fragments/personalId/PersonalIdAccountConfigSetNewBackupCodeFragment.kt +++ b/app/src/org/commcare/fragments/personalId/PersonalIdAccountConfigSetNewBackupCodeFragment.kt @@ -2,10 +2,13 @@ package org.commcare.fragments.personalId import org.commcare.connect.ConnectConstants import org.commcare.dalvik.R +import org.commcare.google.services.analytics.AnalyticsParamValue import org.commcare.personalId.profile.BasePersonalIdSetNewBackupCodeFragment import org.commcare.views.dialogs.StandardAlertDialog class PersonalIdAccountConfigSetNewBackupCodeFragment : BasePersonalIdSetNewBackupCodeFragment() { + override fun analyticsWorkflow(): String = AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_RECOVERY_ACCOUNT_CONFIG + override fun navigateToMessageDisplay( title: String, message: String?, diff --git a/app/src/org/commcare/fragments/personalId/PersonalIdBackupCodeFragment.kt b/app/src/org/commcare/fragments/personalId/PersonalIdBackupCodeFragment.kt index 63b6bdf6a2..743c89cfef 100644 --- a/app/src/org/commcare/fragments/personalId/PersonalIdBackupCodeFragment.kt +++ b/app/src/org/commcare/fragments/personalId/PersonalIdBackupCodeFragment.kt @@ -13,6 +13,7 @@ import org.commcare.google.services.analytics.AnalyticsParamValue import org.commcare.google.services.analytics.FirebaseAnalyticsUtil import org.commcare.personalId.PersonalIdRecoveryCompleter import org.commcare.personalId.PersonalIdUserPreferences +import org.commcare.utils.AccountSecurityAnalyticsMapper import org.commcare.utils.MediaUtil import java.util.Date @@ -70,6 +71,13 @@ class PersonalIdBackupCodeFragment : BasePersonalIdBackupCodeFragment() { } override fun handleForgotBackupCode() { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AccountSecurityAnalyticsMapper.workflowParam(EmailWorkFlow.FORGOT_BACKUP_CODE_RECOVERY), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_FORGOT_CODE_STARTED, + null, + null, + null, + ) navigate( PersonalIdBackupCodeFragmentDirections .actionPersonalidBackupCodeToSendEmailOtp( @@ -160,6 +168,13 @@ class PersonalIdBackupCodeFragment : BasePersonalIdBackupCodeFragment() { private fun navigateToEmail() { PersonalIdUserPreferences.setLastEmailOfferDate(Date()) val emailWorkFlow = if (isRecovery) EmailWorkFlow.RECOVERY else EmailWorkFlow.REGISTRATION + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AccountSecurityAnalyticsMapper.workflowParam(emailWorkFlow), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_EMAIL_PROMPT_SHOWN, + null, + null, + null, + ) navigate( PersonalIdBackupCodeFragmentDirections .actionPersonalidBackupcodeToPersonalidEmail(emailWorkFlow), diff --git a/app/src/org/commcare/fragments/personalId/PersonalIdEmailVerificationForgotBackupCodeFragment.kt b/app/src/org/commcare/fragments/personalId/PersonalIdEmailVerificationForgotBackupCodeFragment.kt index 7498ae5c3a..cbfc0f1597 100644 --- a/app/src/org/commcare/fragments/personalId/PersonalIdEmailVerificationForgotBackupCodeFragment.kt +++ b/app/src/org/commcare/fragments/personalId/PersonalIdEmailVerificationForgotBackupCodeFragment.kt @@ -1,6 +1,9 @@ package org.commcare.fragments.personalId import androidx.navigation.findNavController +import org.commcare.google.services.analytics.AnalyticsParamValue +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil +import org.commcare.utils.AccountSecurityAnalyticsMapper /** * Email verification fragment for the forgot backup code flow for already signed-in users. @@ -17,11 +20,20 @@ class PersonalIdEmailVerificationForgotBackupCodeFragment : BasePersonalIdEmailV override fun resolveEmailOtpRequestCount(): Int = args().emailOtpRequestCount override fun onEmailVerified() { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AccountSecurityAnalyticsMapper.workflowParam(resolveWorkflow()), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_RECOVERY_COMPLETED, + AnalyticsParamValue.OTP_OUTCOME_SUCCESS, + null, + null, + ) binding.root .findNavController() .navigate( PersonalIdEmailVerificationForgotBackupCodeFragmentDirections - .actionEmailVerificationForgotBackupCodeToSetNewBackupCode(), + .actionEmailVerificationForgotBackupCodeToSetNewBackupCode( + BackupCodeWorkflow.FORGOT_BACKUP_CODE, + ), ) } } diff --git a/app/src/org/commcare/fragments/personalId/PersonalIdProfileEmailVerificationFragment.kt b/app/src/org/commcare/fragments/personalId/PersonalIdProfileEmailVerificationFragment.kt index 8faca3ffd1..1b1560772b 100644 --- a/app/src/org/commcare/fragments/personalId/PersonalIdProfileEmailVerificationFragment.kt +++ b/app/src/org/commcare/fragments/personalId/PersonalIdProfileEmailVerificationFragment.kt @@ -4,6 +4,9 @@ import androidx.navigation.findNavController import org.commcare.activities.CommCareActivity import org.commcare.connect.database.ConnectUserDatabaseUtil import org.commcare.dalvik.R +import org.commcare.google.services.analytics.AnalyticsParamValue +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil +import org.commcare.utils.AccountSecurityAnalyticsMapper import org.commcare.views.dialogs.StandardAlertDialog /** @@ -24,6 +27,13 @@ class PersonalIdProfileEmailVerificationFragment : BasePersonalIdEmailVerificati val user = ConnectUserDatabaseUtil.getUser() user.email = enteredEmail ConnectUserDatabaseUtil.storeUser(user) + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AccountSecurityAnalyticsMapper.workflowParam(resolveWorkflow()), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_EMAIL_CHANGED, + AnalyticsParamValue.OTP_OUTCOME_SUCCESS, + null, + null, + ) showEmailAddedSuccessDialog() } diff --git a/app/src/org/commcare/fragments/personalId/PersonalIdProfileSendEmailOtpFragment.kt b/app/src/org/commcare/fragments/personalId/PersonalIdProfileSendEmailOtpFragment.kt index 86727cedac..583eadcfaf 100644 --- a/app/src/org/commcare/fragments/personalId/PersonalIdProfileSendEmailOtpFragment.kt +++ b/app/src/org/commcare/fragments/personalId/PersonalIdProfileSendEmailOtpFragment.kt @@ -5,6 +5,9 @@ import androidx.appcompat.app.AppCompatActivity import androidx.navigation.findNavController import org.commcare.android.database.connect.models.PersonalIdSessionData import org.commcare.dalvik.R +import org.commcare.google.services.analytics.AnalyticsParamValue +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil +import org.commcare.utils.AccountSecurityAnalyticsMapper /** * Screen that sends an email OTP to the user during the profile (existing-user) flow and @@ -20,6 +23,13 @@ class PersonalIdProfileSendEmailOtpFragment : BasePersonalIdSendEmailOtpFragment binding.sendOtpTitle.setText(R.string.personalid_send_email_otp_pending_backup_code_title) binding.sendOtpSubtitle.setText(R.string.personalid_send_email_otp_pending_backup_code_subtitle) binding.sendOtpSubtitle.visibility = View.VISIBLE + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AccountSecurityAnalyticsMapper.workflowParam(workflow), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_SET_CODE_LAUNCHED_ON_STARTUP, + null, + null, + null, + ) } else if (workflow == EmailWorkFlow.EXISTING_USER) { binding.sendOtpTitle.setText(R.string.personalid_email_verification_title) } diff --git a/app/src/org/commcare/google/services/analytics/AnalyticsParamValue.java b/app/src/org/commcare/google/services/analytics/AnalyticsParamValue.java index 43ecebaf47..0d878145e0 100644 --- a/app/src/org/commcare/google/services/analytics/AnalyticsParamValue.java +++ b/app/src/org/commcare/google/services/analytics/AnalyticsParamValue.java @@ -247,6 +247,53 @@ public class AnalyticsParamValue { "email_verification_failure_retry"; public static final String USER_PROMPT_INFO_MANAGE_PROFILE_EMAIL_UPDATE = "manage_profile_email_update"; + public static final String USER_PROMPT_TYPE_BACKUP_CODE = "backup_code"; + public static final String USER_PROMPT_ACTION_SHOWN = "shown"; + public static final String USER_PROMPT_ACTION_FORGOT = "forgot"; + public static final String USER_PROMPT_INFO_BACKUP_CODE_REMINDER = "backup_code_reminder"; + public static final String USER_PROMPT_INFO_SET_BACKUP_CODE_REMINDER = + "set_backup_code_reminder"; + public static final String USER_PROMPT_INFO_SET_NEW_BACKUP_CODE_ABANDON = + "set_new_backup_code_abandon"; + + // Param values for account security analytics + public static final String ACCOUNT_SECURITY_WORKFLOW_CHANGE_BACKUP_CODE = "change_backup_code"; + public static final String ACCOUNT_SECURITY_WORKFLOW_RECOVERY_ACCOUNT_CONFIG = + "recovery_account_config"; + public static final String ACCOUNT_SECURITY_WORKFLOW_RECOVERY_EXISTING_USER = + "recovery_existing_user"; + public static final String ACCOUNT_SECURITY_WORKFLOW_RECOVERY_PENDING_CODE = + "recovery_pending_code"; + public static final String ACCOUNT_SECURITY_WORKFLOW_BACKUP_CODE_REMINDER = + "backup_code_reminder"; + public static final String ACCOUNT_SECURITY_WORKFLOW_EMAIL_CHANGE = "email_change"; + public static final String ACCOUNT_SECURITY_WORKFLOW_EMAIL_CHANGE_BACKUP_CODE = + "email_change_backup_code"; + public static final String ACCOUNT_SECURITY_WORKFLOW_EMAIL_CHANGE_PHONE_OTP = + "email_change_phone_otp"; + public static final String ACCOUNT_SECURITY_WORKFLOW_REGISTRATION = "registration"; + public static final String ACCOUNT_SECURITY_WORKFLOW_ACCOUNT_RECOVERY = "account_recovery"; + + public static final String ACCOUNT_SECURITY_EVENT_CONFIRM_CODE_SHOWN = "confirm_code_shown"; + public static final String ACCOUNT_SECURITY_EVENT_CONFIRM_CODE_ATTEMPT = "confirm_code_attempt"; + public static final String ACCOUNT_SECURITY_EVENT_CONFIRM_CODE_MAX_ATTEMPTS = + "confirm_code_max_attempts"; + public static final String ACCOUNT_SECURITY_EVENT_FORGOT_CODE_STARTED = "forgot_code_started"; + public static final String ACCOUNT_SECURITY_EVENT_SET_NEW_CODE_SHOWN = "set_new_code_shown"; + public static final String ACCOUNT_SECURITY_EVENT_SET_NEW_CODE_ATTEMPT = "set_new_code_attempt"; + public static final String ACCOUNT_SECURITY_EVENT_SEND_EMAIL_OTP_SHOWN = "send_email_otp_shown"; + public static final String ACCOUNT_SECURITY_EVENT_VERIFY_EMAIL_OTP_SHOWN = + "verify_email_otp_shown"; + public static final String ACCOUNT_SECURITY_EVENT_VERIFY_EMAIL_OTP_MAX_ATTEMPTS = + "verify_email_otp_max_attempts"; + public static final String ACCOUNT_SECURITY_EVENT_EMAIL_PROMPT_SHOWN = "email_prompt_shown"; + public static final String ACCOUNT_SECURITY_EVENT_PHONE_OTP_REQUIRED = "phone_otp_required"; + public static final String ACCOUNT_SECURITY_EVENT_PHONE_OTP_MAX_ATTEMPTS = + "phone_otp_max_attempts"; + public static final String ACCOUNT_SECURITY_EVENT_SET_CODE_LAUNCHED_ON_STARTUP = + "set_code_launched_on_startup"; + public static final String ACCOUNT_SECURITY_EVENT_RECOVERY_COMPLETED = "recovery_completed"; + public static final String ACCOUNT_SECURITY_EVENT_EMAIL_CHANGED = "email_changed"; // Param values for SMS invite link analytics public static final String OPP_INVITE_LINK = "opp_invite_link"; diff --git a/app/src/org/commcare/google/services/analytics/CCAnalyticsEvent.java b/app/src/org/commcare/google/services/analytics/CCAnalyticsEvent.java index 622fad27bb..1a296bf768 100644 --- a/app/src/org/commcare/google/services/analytics/CCAnalyticsEvent.java +++ b/app/src/org/commcare/google/services/analytics/CCAnalyticsEvent.java @@ -58,4 +58,6 @@ public class CCAnalyticsEvent { static final String PERSONAL_ID_RELEASE_TOGGLES = "personal_id_release_toggles"; static final String USER_PROMPT = "user_prompt"; static final String EXTERNAL_APP_LAUNCH = "external_app_launch"; + static final String PERSONAL_ID_ACCOUNT_SECURITY_ACTION = + "personalid_account_security_action"; } diff --git a/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java b/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java index 4b226d2717..bcf7146053 100644 --- a/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java +++ b/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java @@ -685,6 +685,31 @@ public static void reportPersonalIDContinueClicked(String screenName, @Nullable reportEvent(CCAnalyticsEvent.PERSONAL_ID_CONTINUE_CLICKED, params); } + /** + * Reports a step in a PersonalID backup-code or email journey. {@code workflow} says which + * journey (see {@link org.commcare.utils.AccountSecurityAnalyticsMapper}), {@code eventType} + * which step; the remaining params are omitted when null. + */ + public static void reportPersonalIdAccountSecurityAction(String workflow, + String eventType, + @Nullable String outcome, + @Nullable String reason, + @Nullable Integer failedAttempts) { + Bundle params = new Bundle(); + params.putString(CCAnalyticsParam.OTP_WORKFLOW, workflow); + params.putString(CCAnalyticsParam.OTP_EVENT_TYPE, eventType); + if (outcome != null) { + params.putString(CCAnalyticsParam.OTP_OUTCOME, outcome); + } + if (reason != null) { + params.putString(CCAnalyticsParam.REASON, reason); + } + if (failedAttempts != null) { + params.putLong(CCAnalyticsParam.OTP_FAILED_ATTEMPTS, failedAttempts); + } + reportEvent(CCAnalyticsEvent.PERSONAL_ID_ACCOUNT_SECURITY_ACTION, params); + } + public static void reportUserPromptEvent(String type, String action, String info) { Bundle params = new Bundle(); params.putString(CCAnalyticsParam.USER_PROMPT_TYPE, type); diff --git a/app/src/org/commcare/personalId/BackupCodeReminderDialog.kt b/app/src/org/commcare/personalId/BackupCodeReminderDialog.kt index 5cd9feaeb3..4e126391d8 100644 --- a/app/src/org/commcare/personalId/BackupCodeReminderDialog.kt +++ b/app/src/org/commcare/personalId/BackupCodeReminderDialog.kt @@ -14,6 +14,8 @@ import org.commcare.connect.database.ConnectUserDatabaseUtil import org.commcare.dalvik.R import org.commcare.dalvik.databinding.DialogBackupCodeReminderBinding import org.commcare.fragments.personalId.BasePersonalIdBackupCodeFragment.Companion.BACKUP_CODE_LENGTH +import org.commcare.google.services.analytics.AnalyticsParamValue +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil import org.commcare.views.connect.bindVisibilityToggle import org.commcare.views.dialogs.CustomViewAlertDialog import org.commcare.views.dialogs.StandardAlertDialog @@ -32,9 +34,18 @@ class BackupCodeReminderDialog private constructor( private fun setupListeners() { binding.backupCodeView.setOnCodeChangedListener { onCodeChanged(it) } - binding.confirmButton.setOnClickListener { onConfirmClicked() } - binding.skipButton.setOnClickListener { onSkipClicked() } - binding.forgotButton.setOnClickListener { onForgotClicked() } + binding.confirmButton.setOnClickListener { + reportReminderPrompt(AnalyticsParamValue.USER_PROMPT_ACTION_ACCEPT) + onConfirmClicked() + } + binding.skipButton.setOnClickListener { + reportReminderPrompt(AnalyticsParamValue.USER_PROMPT_ACTION_SKIP) + onSkipClicked() + } + binding.forgotButton.setOnClickListener { + reportReminderPrompt(AnalyticsParamValue.USER_PROMPT_ACTION_FORGOT) + onForgotClicked() + } binding.backupCodeView.bindVisibilityToggle(binding.backupCodeVisibilityToggle) } @@ -48,11 +59,18 @@ class BackupCodeReminderDialog private constructor( private fun onConfirmClicked() { val entered = binding.backupCodeView.codeValue if (entered == user.pin) { + reportConfirmAttempt(AnalyticsParamValue.OTP_OUTCOME_SUCCESS, null) dismiss() Toast.makeText(context, R.string.personalid_backup_code_reminder_success_toast, Toast.LENGTH_SHORT).show() } else { failedAttempts++ + reportConfirmAttempt(AnalyticsParamValue.OTP_OUTCOME_FAILURE, failedAttempts) if (failedAttempts >= MAX_ATTEMPTS) { + reportReminderEvent( + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_CONFIRM_CODE_MAX_ATTEMPTS, + null, + failedAttempts, + ) Toast.makeText(context, R.string.personalid_backup_code_reminder_max_attempts_toast, Toast.LENGTH_LONG).show() onForgotClicked() } else { @@ -70,6 +88,35 @@ class BackupCodeReminderDialog private constructor( ConnectNavHelper.goToProfile(context, initiateBackupCodeRecovery = true) } + private fun reportReminderPrompt(action: String) { + reportPrompt(action, AnalyticsParamValue.USER_PROMPT_INFO_BACKUP_CODE_REMINDER) + } + + private fun reportConfirmAttempt( + outcome: String, + failedAttempts: Int?, + ) { + reportReminderEvent( + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_CONFIRM_CODE_ATTEMPT, + outcome, + failedAttempts, + ) + } + + private fun reportReminderEvent( + eventType: String, + outcome: String?, + failedAttempts: Int?, + ) { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_BACKUP_CODE_REMINDER, + eventType, + outcome, + null, + failedAttempts, + ) + } + private fun showError(attemptsRemaining: Int) { binding.errorBanner.visibility = View.VISIBLE binding.errorMessage.text = context.getString(R.string.personalid_backup_code_reminder_incorrect, attemptsRemaining) @@ -99,6 +146,10 @@ class BackupCodeReminderDialog private constructor( showSetBackupCodePrompt(activity) } else { val binding = DialogBackupCodeReminderBinding.inflate(LayoutInflater.from(activity)) + reportPrompt( + AnalyticsParamValue.USER_PROMPT_ACTION_SHOWN, + AnalyticsParamValue.USER_PROMPT_INFO_BACKUP_CODE_REMINDER, + ) activity.showAlertDialog(BackupCodeReminderDialog(binding, activity, user)) } } @@ -110,13 +161,31 @@ class BackupCodeReminderDialog private constructor( activity.getString(R.string.personalid_set_backup_code_reminder_message), ) dialog.setPositiveButton(activity.getString(R.string.personalid_backup_code_reminder_confirm)) { _, _ -> + reportSetBackupCodePrompt(AnalyticsParamValue.USER_PROMPT_ACTION_ACCEPT) activity.dismissAlertDialog() ConnectNavHelper.goToProfileForPendingBackupCode(activity) } dialog.setNegativeButton(activity.getString(R.string.personalid_backup_code_reminder_skip)) { _, _ -> + reportSetBackupCodePrompt(AnalyticsParamValue.USER_PROMPT_ACTION_SKIP) activity.dismissAlertDialog() } + reportSetBackupCodePrompt(AnalyticsParamValue.USER_PROMPT_ACTION_SHOWN) activity.showAlertDialog(dialog) } + + private fun reportSetBackupCodePrompt(action: String) { + reportPrompt(action, AnalyticsParamValue.USER_PROMPT_INFO_SET_BACKUP_CODE_REMINDER) + } + + private fun reportPrompt( + action: String, + info: String, + ) { + FirebaseAnalyticsUtil.reportUserPromptEvent( + AnalyticsParamValue.USER_PROMPT_TYPE_BACKUP_CODE, + action, + info, + ) + } } } diff --git a/app/src/org/commcare/personalId/profile/BasePersonalIdSetNewBackupCodeFragment.kt b/app/src/org/commcare/personalId/profile/BasePersonalIdSetNewBackupCodeFragment.kt index 0d700a7d51..5569764772 100644 --- a/app/src/org/commcare/personalId/profile/BasePersonalIdSetNewBackupCodeFragment.kt +++ b/app/src/org/commcare/personalId/profile/BasePersonalIdSetNewBackupCodeFragment.kt @@ -12,11 +12,16 @@ import org.commcare.connect.network.base.PersonalIdOrConnectApiErrorHandler import org.commcare.connect.network.personalId.PersonalIdApiHandler import org.commcare.dalvik.R import org.commcare.fragments.personalId.BasePersonalIdBackupCodeFragment +import org.commcare.google.services.analytics.AnalyticsParamValue +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil import org.commcare.personalId.PersonalIdUnlocker import org.commcare.personalId.UnlockPolicy import org.commcare.views.dialogs.StandardAlertDialog abstract class BasePersonalIdSetNewBackupCodeFragment : BasePersonalIdBackupCodeFragment() { + /** The `workflow` analytics param, which differs per graph and per entry into this screen. */ + abstract fun analyticsWorkflow(): String + override fun onViewCreated( view: View, savedInstanceState: Bundle?, @@ -43,9 +48,11 @@ abstract class BasePersonalIdSetNewBackupCodeFragment : BasePersonalIdBackupCode getString(R.string.personalid_set_new_backup_code_abandon_message), ) dialog.setPositiveButton(getString(R.string.personalid_set_new_backup_code_abandon_positive)) { d, _ -> + reportAbandonPrompt(AnalyticsParamValue.USER_PROMPT_ACTION_CANCEL) d.dismiss() } dialog.setNegativeButton(getString(R.string.personalid_set_new_backup_code_abandon_negative)) { d, _ -> + reportAbandonPrompt(AnalyticsParamValue.USER_PROMPT_ACTION_ACCEPT) d.dismiss() onAbandon() } @@ -53,6 +60,14 @@ abstract class BasePersonalIdSetNewBackupCodeFragment : BasePersonalIdBackupCode dialog.showNonPersistentDialog(requireActivity()) } + private fun reportAbandonPrompt(action: String) { + FirebaseAnalyticsUtil.reportUserPromptEvent( + AnalyticsParamValue.USER_PROMPT_TYPE_BACKUP_CODE, + action, + AnalyticsParamValue.USER_PROMPT_INFO_SET_NEW_BACKUP_CODE_ABANDON, + ) + } + protected open fun onAbandon() { findNavController().popBackStack() if (findNavController().currentDestination == null) { @@ -71,6 +86,13 @@ abstract class BasePersonalIdSetNewBackupCodeFragment : BasePersonalIdBackupCode showConfirmCode = true, subtitle = getString(R.string.connect_backup_code_remember, BACKUP_CODE_LENGTH), ) + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + analyticsWorkflow(), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_SET_NEW_CODE_SHOWN, + null, + null, + null, + ) } override fun handleBackupCodeSubmission() { @@ -112,6 +134,7 @@ abstract class BasePersonalIdSetNewBackupCodeFragment : BasePersonalIdBackupCode ) { user.pin = backupCode ConnectUserDatabaseUtil.storeUser(user) + reportSetNewCodeAttempt(AnalyticsParamValue.OTP_OUTCOME_SUCCESS, null) showSuccess() } @@ -121,6 +144,7 @@ abstract class BasePersonalIdSetNewBackupCodeFragment : BasePersonalIdBackupCode errorCode: PersonalIdOrConnectApiErrorCodes, t: Throwable?, ) { + reportSetNewCodeAttempt(AnalyticsParamValue.OTP_OUTCOME_FAILURE, errorCode.name) showError( PersonalIdOrConnectApiErrorHandler.handle( requireActivity(), @@ -130,4 +154,17 @@ abstract class BasePersonalIdSetNewBackupCodeFragment : BasePersonalIdBackupCode ) enableContinueButton(true) } + + private fun reportSetNewCodeAttempt( + outcome: String, + reason: String?, + ) { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + analyticsWorkflow(), + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_SET_NEW_CODE_ATTEMPT, + outcome, + reason, + null, + ) + } } diff --git a/app/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeFragment.kt b/app/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeFragment.kt index e83e59c44b..19f0208615 100644 --- a/app/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeFragment.kt +++ b/app/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeFragment.kt @@ -7,7 +7,10 @@ import org.commcare.connect.database.ConnectUserDatabaseUtil import org.commcare.dalvik.R import org.commcare.fragments.personalId.BasePersonalIdBackupCodeFragment import org.commcare.fragments.personalId.EmailWorkFlow +import org.commcare.google.services.analytics.AnalyticsParamValue +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil import org.commcare.personalId.PersonalIdUserPreferences +import org.commcare.utils.AccountSecurityAnalyticsMapper class PersonalIdProfileBackupCodeFragment : BasePersonalIdBackupCodeFragment() { private val args by lazy { PersonalIdProfileBackupCodeFragmentArgs.fromBundle(requireArguments()) } @@ -15,6 +18,8 @@ class PersonalIdProfileBackupCodeFragment : BasePersonalIdBackupCodeFragment() { private var isLocked = false + private val analyticsWorkflow get() = AccountSecurityAnalyticsMapper.workflowParam(args.backupCodeWorkflow) + override fun onViewCreated( view: View, savedInstanceState: Bundle?, @@ -32,6 +37,13 @@ class PersonalIdProfileBackupCodeFragment : BasePersonalIdBackupCodeFragment() { subtitle = getString(R.string.connect_backup_code_message), ) binding.personalidForgotBackupCode.visibility = View.VISIBLE + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + analyticsWorkflow, + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_CONFIRM_CODE_SHOWN, + null, + null, + null, + ) } override fun onCodeChanged() { @@ -39,6 +51,13 @@ class PersonalIdProfileBackupCodeFragment : BasePersonalIdBackupCodeFragment() { } override fun handleForgotBackupCode() { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + analyticsWorkflow, + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_FORGOT_CODE_STARTED, + null, + null, + null, + ) val email = ConnectUserDatabaseUtil.getUser().email when { !email.isNullOrEmpty() -> { @@ -77,6 +96,7 @@ class PersonalIdProfileBackupCodeFragment : BasePersonalIdBackupCodeFragment() { val storedBackupCode = ConnectUserDatabaseUtil.getUser()?.pin if (enteredCode == storedBackupCode) { PersonalIdUserPreferences.clearBackupCodeLockout() + reportConfirmAttempt(AnalyticsParamValue.OTP_OUTCOME_SUCCESS, null) if (args.emailWorkflow == EmailWorkFlow.EXISTING_USER) { navigateToEmailOtpFragment() } else { @@ -84,7 +104,15 @@ class PersonalIdProfileBackupCodeFragment : BasePersonalIdBackupCodeFragment() { } } else { val attempts = PersonalIdUserPreferences.recordBackupCodeFailure() + reportConfirmAttempt(AnalyticsParamValue.OTP_OUTCOME_FAILURE, attempts) if (attempts >= MAX_ATTEMPTS) { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + analyticsWorkflow, + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_CONFIRM_CODE_MAX_ATTEMPTS, + null, + null, + attempts, + ) PersonalIdUserPreferences.triggerBackupCodeLockout() enterLockedState() } else { @@ -101,8 +129,23 @@ class PersonalIdProfileBackupCodeFragment : BasePersonalIdBackupCodeFragment() { findNavController().navigate(directions) } + private fun reportConfirmAttempt( + outcome: String, + failedAttempts: Int?, + ) { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + analyticsWorkflow, + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_CONFIRM_CODE_ATTEMPT, + outcome, + null, + failedAttempts, + ) + } + private fun navigateToSetNewBackupCode() { - val directions = PersonalIdProfileBackupCodeFragmentDirections.actionProfileBackupCodeToSetNewBackupCode() + val directions = + PersonalIdProfileBackupCodeFragmentDirections + .actionProfileBackupCodeToSetNewBackupCode(args.backupCodeWorkflow) findNavController().navigate(directions) } diff --git a/app/src/org/commcare/personalId/profile/PersonalIdProfileEditFragment.kt b/app/src/org/commcare/personalId/profile/PersonalIdProfileEditFragment.kt index 1f1a89d8ab..a0955548f9 100644 --- a/app/src/org/commcare/personalId/profile/PersonalIdProfileEditFragment.kt +++ b/app/src/org/commcare/personalId/profile/PersonalIdProfileEditFragment.kt @@ -14,6 +14,7 @@ import org.commcare.connect.network.base.PersonalIdOrConnectApiErrorHandler import org.commcare.connect.network.personalId.PersonalIdApiHandler import org.commcare.dalvik.R import org.commcare.dalvik.databinding.PersonalidProfileEditScreenBinding +import org.commcare.fragments.personalId.BackupCodeWorkflow import org.commcare.fragments.personalId.EmailWorkFlow import org.commcare.google.services.analytics.AnalyticsParamValue import org.commcare.google.services.analytics.FirebaseAnalyticsUtil @@ -164,12 +165,21 @@ class PersonalIdProfileEditFragment : BasePersonalIdProfileFragment() { _binding ?: return val directions = if (viewModel.user.pin.isNullOrEmpty()) { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_EMAIL_CHANGE_PHONE_OTP, + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_PHONE_OTP_REQUIRED, + null, + null, + null, + ) PersonalIdProfileEditFragmentDirections .actionProfileEditToProfileSendPhoneOtp(pendingEmail) } else { PersonalIdProfileEditFragmentDirections - .actionProfileEditToBackupCode(EmailWorkFlow.EXISTING_USER) - .setPendingEmail(pendingEmail) + .actionProfileEditToBackupCode( + EmailWorkFlow.EXISTING_USER, + BackupCodeWorkflow.EMAIL_CHANGE, + ).setPendingEmail(pendingEmail) } findNavController().navigate(directions) } diff --git a/app/src/org/commcare/personalId/profile/PersonalIdProfileFragment.kt b/app/src/org/commcare/personalId/profile/PersonalIdProfileFragment.kt index f5c3846437..2ba2fdfdc1 100644 --- a/app/src/org/commcare/personalId/profile/PersonalIdProfileFragment.kt +++ b/app/src/org/commcare/personalId/profile/PersonalIdProfileFragment.kt @@ -14,6 +14,7 @@ import androidx.navigation.fragment.findNavController import org.commcare.connect.database.ConnectUserDatabaseUtil import org.commcare.dalvik.R import org.commcare.dalvik.databinding.PersonalidProfileScreenBinding +import org.commcare.fragments.personalId.BackupCodeWorkflow import org.commcare.fragments.personalId.EmailWorkFlow class PersonalIdProfileFragment : BasePersonalIdProfileFragment() { @@ -69,7 +70,10 @@ class PersonalIdProfileFragment : BasePersonalIdProfileFragment() { private fun navigateToProfileBackupCode() { findNavController().navigate( PersonalIdProfileFragmentDirections - .actionProfileToProfileBackupCode(EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER), + .actionProfileToProfileBackupCode( + EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER, + BackupCodeWorkflow.CHANGE_BACKUP_CODE, + ), ) } diff --git a/app/src/org/commcare/personalId/profile/PersonalIdProfilePhoneVerificationFragment.kt b/app/src/org/commcare/personalId/profile/PersonalIdProfilePhoneVerificationFragment.kt index c82d05eb2f..0acae7cdf2 100644 --- a/app/src/org/commcare/personalId/profile/PersonalIdProfilePhoneVerificationFragment.kt +++ b/app/src/org/commcare/personalId/profile/PersonalIdProfilePhoneVerificationFragment.kt @@ -8,6 +8,8 @@ import org.commcare.core.network.AuthInfo import org.commcare.fragments.personalId.BasePersonalIdPhoneVerificationFragment import org.commcare.fragments.personalId.EmailWorkFlow import org.commcare.fragments.personalId.PersonalIdWorkflow +import org.commcare.google.services.analytics.AnalyticsParamValue +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil import org.commcare.utils.AttemptCounter import org.commcare.utils.CommCareAttemptCounter import org.commcare.utils.OtpManager @@ -53,6 +55,16 @@ class PersonalIdProfilePhoneVerificationFragment : BasePersonalIdPhoneVerificati override fun showChangeNumberLink(): Boolean = false + override fun onOtpAttemptsExhausted() { + FirebaseAnalyticsUtil.reportPersonalIdAccountSecurityAction( + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_EMAIL_CHANGE_PHONE_OTP, + AnalyticsParamValue.ACCOUNT_SECURITY_EVENT_PHONE_OTP_MAX_ATTEMPTS, + null, + null, + attemptTracker.failedAttempts, + ) + } + override fun onOtpVerified() { findNavController().navigate( PersonalIdProfilePhoneVerificationFragmentDirections diff --git a/app/src/org/commcare/personalId/profile/PersonalIdProfileSetNewBackupCodeFragment.kt b/app/src/org/commcare/personalId/profile/PersonalIdProfileSetNewBackupCodeFragment.kt index 37d9d2edca..b62d5d8be4 100644 --- a/app/src/org/commcare/personalId/profile/PersonalIdProfileSetNewBackupCodeFragment.kt +++ b/app/src/org/commcare/personalId/profile/PersonalIdProfileSetNewBackupCodeFragment.kt @@ -3,8 +3,13 @@ package org.commcare.personalId.profile import android.widget.Toast import androidx.navigation.fragment.findNavController import org.commcare.dalvik.R +import org.commcare.utils.AccountSecurityAnalyticsMapper class PersonalIdProfileSetNewBackupCodeFragment : BasePersonalIdSetNewBackupCodeFragment() { + private val args by lazy { PersonalIdProfileSetNewBackupCodeFragmentArgs.fromBundle(requireArguments()) } + + override fun analyticsWorkflow(): String = AccountSecurityAnalyticsMapper.workflowParam(args.backupCodeWorkflow) + override fun showSuccess() { Toast .makeText( diff --git a/app/src/org/commcare/utils/AccountSecurityAnalyticsMapper.kt b/app/src/org/commcare/utils/AccountSecurityAnalyticsMapper.kt new file mode 100644 index 0000000000..33ada7cf79 --- /dev/null +++ b/app/src/org/commcare/utils/AccountSecurityAnalyticsMapper.kt @@ -0,0 +1,63 @@ +package org.commcare.utils + +import org.commcare.fragments.personalId.BackupCodeWorkflow +import org.commcare.fragments.personalId.EmailWorkFlow +import org.commcare.google.services.analytics.AnalyticsParamValue + +/** + * Normalizes the PersonalID backup-code and email journeys into the stable analytics strings + * emitted with the {@code personalid_account_security_action} event. + */ +object AccountSecurityAnalyticsMapper { + /** + * Maps an [EmailWorkFlow] to the `workflow` param, which says which journey the user is in + * when the shared email and backup-code screens report an event. + */ + @JvmStatic + fun workflowParam(workflow: EmailWorkFlow): String = + when (workflow) { + EmailWorkFlow.REGISTRATION -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_REGISTRATION + } + + EmailWorkFlow.RECOVERY -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_ACCOUNT_RECOVERY + } + + EmailWorkFlow.EXISTING_USER -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_EMAIL_CHANGE + } + + EmailWorkFlow.FORGOT_BACKUP_CODE_RECOVERY -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_RECOVERY_ACCOUNT_CONFIG + } + + EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_RECOVERY_EXISTING_USER + } + + EmailWorkFlow.PENDING_BACKUP_CODE -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_RECOVERY_PENDING_CODE + } + } + + /** + * Maps a [BackupCodeWorkflow] to the `workflow` param, for the backup-code screens that + * several journeys share. + */ + @JvmStatic + fun workflowParam(workflow: BackupCodeWorkflow): String = + when (workflow) { + BackupCodeWorkflow.CHANGE_BACKUP_CODE -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_CHANGE_BACKUP_CODE + } + + BackupCodeWorkflow.EMAIL_CHANGE -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_EMAIL_CHANGE_BACKUP_CODE + } + + BackupCodeWorkflow.FORGOT_BACKUP_CODE -> { + AnalyticsParamValue.ACCOUNT_SECURITY_WORKFLOW_RECOVERY_EXISTING_USER + } + } +} diff --git a/app/unit-tests/src/org/commcare/personalId/profile/BasePersonalIdSetNewBackupCodeFragmentTest.kt b/app/unit-tests/src/org/commcare/personalId/profile/BasePersonalIdSetNewBackupCodeFragmentTest.kt index e31968aee5..800eb0dd1f 100644 --- a/app/unit-tests/src/org/commcare/personalId/profile/BasePersonalIdSetNewBackupCodeFragmentTest.kt +++ b/app/unit-tests/src/org/commcare/personalId/profile/BasePersonalIdSetNewBackupCodeFragmentTest.kt @@ -12,6 +12,7 @@ import org.commcare.CommCareTestApplication import org.commcare.connect.PersonalIdManager import org.commcare.connect.database.ConnectUserDatabaseUtil import org.commcare.dalvik.R +import org.commcare.fragments.personalId.BackupCodeWorkflow import org.commcare.fragments.personalId.EmailWorkFlow import org.commcare.personalId.PersonalIdUnlocker import org.commcare.personalId.PersonalIdUserPreferences @@ -43,7 +44,10 @@ class BasePersonalIdSetNewBackupCodeFragmentTest : BasePersonalIdProfileTest() { onUiThread { navController.navigate( PersonalIdProfileFragmentDirections - .actionProfileToProfileBackupCode(EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER), + .actionProfileToProfileBackupCode( + EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER, + BackupCodeWorkflow.CHANGE_BACKUP_CODE, + ), ) } // Enter the current backup code on the confirm screen and click continue diff --git a/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeChangeEmailFragmentTest.kt b/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeChangeEmailFragmentTest.kt index b6367005b6..846e3d9b17 100644 --- a/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeChangeEmailFragmentTest.kt +++ b/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeChangeEmailFragmentTest.kt @@ -8,6 +8,7 @@ import org.commcare.CommCareTestApplication import org.commcare.android.shadows.ShadowPhoneAuthProvider import org.commcare.android.util.FirebaseTestUtils import org.commcare.dalvik.R +import org.commcare.fragments.personalId.BackupCodeWorkflow import org.commcare.fragments.personalId.EmailWorkFlow import org.commcare.fragments.personalId.PersonalIdProfileSendEmailOtpFragmentArgs import org.commcare.personalId.PersonalIdUserPreferences @@ -29,7 +30,7 @@ import org.robolectric.shadows.ShadowLooper class PersonalIdProfileBackupCodeChangeEmailFragmentTest : BasePersonalIdProfileTest() { private val fragmentArgs = PersonalIdProfileBackupCodeFragmentArgs - .Builder(EmailWorkFlow.EXISTING_USER) + .Builder(EmailWorkFlow.EXISTING_USER, BackupCodeWorkflow.EMAIL_CHANGE) .setPendingEmail("grace@example.com") .build() .toBundle() diff --git a/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeFragmentTest.kt b/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeFragmentTest.kt index c9d5665a65..97c822d6f2 100644 --- a/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeFragmentTest.kt +++ b/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfileBackupCodeFragmentTest.kt @@ -7,6 +7,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.android.material.button.MaterialButton import org.commcare.CommCareTestApplication import org.commcare.dalvik.R +import org.commcare.fragments.personalId.BackupCodeWorkflow import org.commcare.fragments.personalId.EmailWorkFlow import org.commcare.personalId.PersonalIdUserPreferences import org.commcare.views.connect.NumericCodeView @@ -30,7 +31,10 @@ class PersonalIdProfileBackupCodeFragmentTest : BasePersonalIdProfileTest() { onUiThread { navController.navigate( PersonalIdProfileFragmentDirections - .actionProfileToProfileBackupCode(EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER), + .actionProfileToProfileBackupCode( + EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER, + BackupCodeWorkflow.CHANGE_BACKUP_CODE, + ), ) } } @@ -171,7 +175,10 @@ class PersonalIdProfileBackupCodeFragmentTest : BasePersonalIdProfileTest() { onUiThread { navController.navigate( PersonalIdProfileFragmentDirections - .actionProfileToProfileBackupCode(EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER), + .actionProfileToProfileBackupCode( + EmailWorkFlow.FORGOT_BACKUP_CODE_EXISTING_USER, + BackupCodeWorkflow.CHANGE_BACKUP_CODE, + ), ) } ShadowLooper.idleMainLooper() diff --git a/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfilePhoneVerificationFragmentTest.kt b/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfilePhoneVerificationFragmentTest.kt index 6f10055ef2..9989a20e34 100644 --- a/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfilePhoneVerificationFragmentTest.kt +++ b/app/unit-tests/src/org/commcare/personalId/profile/PersonalIdProfilePhoneVerificationFragmentTest.kt @@ -24,6 +24,7 @@ import org.commcare.android.util.FirebaseTestUtils import org.commcare.connect.network.personalId.PersonalIdApiEndpoints import org.commcare.core.network.AuthInfo import org.commcare.dalvik.R +import org.commcare.fragments.personalId.BackupCodeWorkflow import org.commcare.fragments.personalId.EmailWorkFlow import org.commcare.fragments.personalId.PersonalIdProfileSendEmailOtpFragmentArgs import org.commcare.network.HttpUtils @@ -293,7 +294,7 @@ class PersonalIdProfilePhoneVerificationFragmentTest : BasePersonalIdProfileTest private fun openFromForgotBackupCode() { val backupCodeArgs = PersonalIdProfileBackupCodeFragmentArgs - .Builder(EmailWorkFlow.EXISTING_USER) + .Builder(EmailWorkFlow.EXISTING_USER, BackupCodeWorkflow.EMAIL_CHANGE) .setPendingEmail(pendingEmail) .build() .toBundle()