-
Notifications
You must be signed in to change notification settings - Fork 934
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Additional "default browser" prompts: metrics #5543
Additional "default browser" prompts: metrics #5543
Conversation
...com/duckduckgo/app/browser/defaultbrowsing/prompts/DefaultBrowserPromptsExperimentMetrics.kt
Outdated
Show resolved
Hide resolved
1397fda
to
cc2c9a8
Compare
f8b2c44
to
ef11c74
Compare
@@ -361,19 +367,21 @@ class BrowserViewModel @Inject constructor( | |||
} | |||
|
|||
fun onSystemDefaultBrowserDialogSuccess() { | |||
defaultBrowserPromptsExperiment.onSystemDefaultBrowserDialogSuccess() | |||
defaultBrowserPromptsExperiment.onSystemDefaultBrowserDialogSuccess( | |||
lastSystemDefaultBrowserDialogTrigger ?: SetAsDefaultActionTrigger.UNKNOWN, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In practice, we should never fall back to UNKNOWN
. I added it as a safeguard because after briefly looking at the problem I couldn't find a non-nullable way to round-trip a parameter through the "activity for result" stack.
Since we can't launch the default browser dialog or system apps and expect them to return a specific parameter back, we have to store it locally. This makes the parameter nullable, which is why we need the UNKNOWN
fallback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make a case of the enum for not being handled and set it as default instead? You are checking for a null parameter, why not checking for the non-handled case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a case in DefaultBrowserPromptsExperimentImpl
to handle the UNKNOWN
type, if that's what you mean. I can also just make the cache field default to UNKNOWN
to get rid of the elvis operator, it won't change anything functionally in the end.
This is ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing blocking, I’ve got a couple questions on the implementation.
@@ -361,19 +367,21 @@ class BrowserViewModel @Inject constructor( | |||
} | |||
|
|||
fun onSystemDefaultBrowserDialogSuccess() { | |||
defaultBrowserPromptsExperiment.onSystemDefaultBrowserDialogSuccess() | |||
defaultBrowserPromptsExperiment.onSystemDefaultBrowserDialogSuccess( | |||
lastSystemDefaultBrowserDialogTrigger ?: SetAsDefaultActionTrigger.UNKNOWN, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not make a case of the enum for not being handled and set it as default instead? You are checking for a null parameter, why not checking for the non-handled case?
} | ||
|
||
override fun onMessageDialogNotNowButtonClicked() { | ||
fireInteractionPixel(AppPixelName.SET_AS_DEFAULT_PROMPT_DISMISSED) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don’t we want to differentiate between dismissing a dialog and actively saying no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pixel is defined as "triggered when user clicks 'Not now' or uses a gesture to close the prompt". In the end, regardless of whether user collapses the dialog or clicks not now, the intention is the same.
} | ||
|
||
override fun onMessageDialogDismissed() { | ||
override fun onMessageDialogCanceled() { | ||
fireInteractionPixel(AppPixelName.SET_AS_DEFAULT_PROMPT_DISMISSED) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still a dismissed pixel, but it’s cancelled instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Dismissed" and "canceled" mean different things in context of an Android dialog, which doesn't translate to what we mean by "dismissed" in context of the pixel. The pixel wants to know when user didn't take the action and closed the dialog either by a gesture or by a "not now" button.
In the Android impl, dialog cancellation represents one of ways to dismiss a dialog, which includes clicking away and collapsing it.
On the other hand, dialog dismissal happens at any time a dialog is removed, including when a gesture is used (so when the dialog is canceled), but also when we're programmatically dismissing a dialog after either button is clicked by the user.
In our case, clicking the "set as default" button on the dialog sends it's own "click" pixel, and we don't want to send a "dismissed" pixel at the same time when we programmatically close the dialog after the user interaction.
So each of the buttons sends its own respective pixel and dismisses the dialog, and we also listen for when dialog is canceled (no button was pressed) and send the respective pixel as well.
@@ -47,8 +47,8 @@ class DefaultBrowserBottomSheetDialog(private val context: Context) : BottomShee | |||
setRoundCorners(dialogInterface) | |||
eventListener?.onShown() | |||
} | |||
setOnDismissListener { | |||
eventListener?.onDismissed() | |||
setOnCancelListener { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what’s the reasoning for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explained in #5543 (comment).
@@ -383,4 +383,9 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName { | |||
DEDICATED_WEBVIEW_URL_EXTRACTION_FAILED("m_dedicated_webview_url_extraction_failed"), | |||
|
|||
BLOCKLIST_TDS_FAILURE("blocklist_experiment_tds_download_failure"), | |||
|
|||
SET_AS_DEFAULT_PROMPT_IMPRESSION("m_set-as-default_prompt_impression"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
either use “-“ or “_” but don’t use a combination of both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using this to treat the new pixels like a tree:
m
set-as-default
prompt
impression
click
dismissed
in-menu
click
I see various examples of that in the codebase, but please let me know if that should cause any issues.
ef11c74
to
4ce4e1b
Compare
Task/Issue URL: https://app.asana.com/0/1201621853593513/1208697958169120/f
Marking as blocked until all pixels are confirmed with the DS team. This is generally ready for a first round of reviews but some additional changes may be still introduced because of that.
Steps to test this PR
You can follow instructions from #5476 to go through the experiment.
Test for below pixels:
experiment_enroll_defaultBrowserAdditionalPrompts202501_variant_2 with params: {enrollmentDate=2025-01-27}
experiment_metrics_defaultBrowserAdditionalPrompts202501_variant_2 with params: {metric=stageImpression, value=stage_1, enrollmentDate=2025-01-27, conversionWindowDays=1-X}
(X
is 20, 40, 60)experiment_metrics_defaultBrowserAdditionalPrompts202501_variant_2 with params: {metric=stageImpression, value=stage_2, enrollmentDate=2025-01-27, conversionWindowDays=1-X}
(X
is 20, 40, 60)m_set-as-default_prompt_dismissed with params: {expVar=variant_2, expStage=stage_1}
m_set-as-default_prompt_click with params: {expVar=variant_2, expStage=stage_1}
m_set-as-default_in-menu_click with params: {expVar=variant_2, expStage=stage_2}
experiment_metrics_defaultBrowserAdditionalPrompts202501_variant_2 with params: {metric=defaultSet, value=stage_2, enrollmentDate=2025-01-27, conversionWindowDays=1-X}
(X
is 20, 40, 60)experiment_metrics_defaultBrowserAdditionalPrompts202501_variant_2 with params: {metric=defaultSetViaCta, value=menu, enrollmentDate=2025-01-27, conversionWindowDays=1-X}
(X
is 20, 40, 60)