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()