-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[Android] "What’s New" promo message: Prompt dispatcher - modal coordinator #7343
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
base: develop
Are you sure you want to change the base?
[Android] "What’s New" promo message: Prompt dispatcher - modal coordinator #7343
Conversation
92ec7e1 to
a7d6647
Compare
f80d95b to
4dd0807
Compare
a7d6647 to
05e9c3e
Compare
4dd0807 to
afd2782
Compare
05e9c3e to
1bfabe0
Compare
afd2782 to
1874dd5
Compare
1bfabe0 to
068f409
Compare
1874dd5 to
998e90e
Compare
366bbc1 to
b411140
Compare
40609a5 to
64ebebb
Compare
| init { | ||
| appCoroutineScope.launch { | ||
| userStageStore.userAppStageFlow().collect { | ||
| evaluate() |
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.
Removed this as it's not really needed and created more issues. I wanted to remove it when I worked on the initial project, but forgot.
| override fun onResume(owner: LifecycleOwner) { | ||
| super.onResume(owner) | ||
| appCoroutineScope.launch { | ||
| evaluate() |
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 now called as part of ModalEvaluatorCoordinator's onResume.
| appCoroutineScope.launch { | ||
| appCoroutineScope.launch(dispatchers.io()) { | ||
| featureSettings = defaultBrowserPromptsFeatureToggles.parseFeatureSettings() | ||
| evaluate() |
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.
Again, not needed, removed.
| defaultBrowserPromptsDataStore.storeShowSetAsDefaultMessageState(action.showMessage) | ||
|
|
||
| return@withLock if (action.showMessageDialog) { | ||
| ModalEvaluator.EvaluationResult.CompletedWithAction |
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 the only place where a modal is shown, hence the only place that returns CompletedWithAction.
| override fun onResume(owner: LifecycleOwner) { | ||
| super.onResume(owner) | ||
| appCoroutineScope.launch { | ||
| evaluate() |
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.
Moved to ModalEvaluatorCoordinator onResume
|
|
||
| intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP | ||
| applicationContext.startActivity(intent) | ||
| return@withContext ModalEvaluator.EvaluationResult.CompletedWithAction |
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.
As in the other evaluator, this is the place a modal is shown, hence returning CompletedWithAction
b411140 to
9c0c2a1
Compare
08e3793 to
091a355
Compare
.../src/main/java/com/duckduckgo/remote/messaging/impl/ui/RemoteMessageModalSurfaceEvaluator.kt
Show resolved
Hide resolved
.../src/main/java/com/duckduckgo/remote/messaging/impl/ui/RemoteMessageModalSurfaceEvaluator.kt
Show resolved
Hide resolved
12c6b27 to
507fc69
Compare
2549ea7 to
f4be73c
Compare
...rc/main/java/com/duckduckgo/modalcoordinator/impl/store/ModalEvaluatorCompletionStoreImpl.kt
Show resolved
Hide resolved
507fc69 to
970a291
Compare
f4be73c to
1045448
Compare
1045448 to
51a045a
Compare
…nd 24-hour blocking.
…ckground timestamp management. Removed dependency to SettingsDataStore.
…thods and update background timestamp retrieval logic.
…OwnerMock usage with evaluate method calls.
…nator and RemoteMessageModalSurfaceEvaluatorImpl.
51a045a to
661ffa2
Compare
cmonfortep
left a comment
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.
Code wise, looks good.
I have some concerns with behavior we introduce, and implicit behavior of the modal. Left some comments about that.
My main concern on the behavior: After a modal is shown, if we restart the app, the modal doesn't show, but it will show again after 24h.
My expectation will be: After the modal is shown, even if we restart the app, it shows again if the modal needs to show (that logic belongs to the modal). Coordinator only makes sure that after the modal is gone, we do a 24h cooldown, if that's the requirement.
|
|
||
| package com.duckduckgo.modalcoordinator.api | ||
|
|
||
| interface ModalEvaluatorCompletionStore { |
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 think we missed the api proposal for this api. From a quick look, not all methods are used outside this api. Exposing the Store in the api also feels not ideal.
If the purpose of modal-coordinator is to avoid multiple modals showing at the same time, the logic around cooldown period or background checks might not need to be exposed. If we need that, we should probably include it when implementing the plugin.
| logcat { "ModalEvaluatorCoordinator: Evaluating '${evaluator.evaluatorId}' (priority ${evaluator.priority})" } | ||
|
|
||
| // Start evaluation | ||
| when (evaluator.evaluate()) { |
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.
Here relies the issue I mentioned on MM about modals blocking themselves if the app restarts, and appearing again after 24h.
Few things I see:
- when
evaluatereturnsCompletedWithActionand we record completion. - completion is confusing, or at least I don't think it matches the definition of completion on every modal. Right now we know (or expect) they have been shown.
- I think the modal itself should track their own state (e.g: RMF will own his own state on the database like scheduled, dismissed, done)
- Here I would just track timestamp and id
One proposal here (not sure if it will work) will be to update the logic so we store the time the modal is shown (like you do now) and the id. If within the 24h threshold, we only call evaluate for the plugin with that Id. That way we allow only the modal that was last shown to appear within that 24h window. Others will need to wait for the cool down to finish.
| return@withContext ModalEvaluator.EvaluationResult.Skipped | ||
| } | ||
|
|
||
| if (!hasMetBackgroundTimeThreshold()) { |
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 don't fully understand why this logic. So the app needs to be in the background for more than 4h in order to show. I would do that if the event is onResume, but if it's an app fresh start, why do we need that? is that a requirement?
| sealed class EvaluationResult { | ||
|
|
||
| /** Evaluation completed and modal was shown/triggered */ | ||
| object CompletedWithAction : EvaluationResult() |
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 think if this is to represent shown we should rename to something that makes that clear.

Task/Issue URL: https://app.asana.com/1/137249556945/project/488551667048375/task/1212329205023394?focus=true
Description
Implemented a new Modal Coordinator system that manages when and how modals are shown in the app. This system:
Steps to test this PR
See detailed testing scenarios and help in the
Testingsection in the description of https://app.asana.com/1/137249556945/project/1200581511062568/task/1212329205023394?focus=trueModal Coordinator System
Default Browser Prompts
Remote Message Modals
Note
Adds a prioritized modal coordination system with 24‑hour cooldown and migrates Remote Message modal and Default Browser prompts to it.
modal-coordinator-apiwithModalEvaluatorandModalEvaluatorCompletionStore.modal-coordinator-implwithModalEvaluatorCoordinator(priority-based, single-run per resume, 24h cooldown) and DataStore-backed completion store.app/build.gradleand depend on API fromremote-messaging-impl.RemoteMessageModalSurfaceEvaluator-> implementsModalEvaluator(priority1), enforces 4h background threshold via completion store, launches modal and clears timestamp.AdditionalDefaultBrowserPromptsImpl-> implementsModalEvaluator(priority2), refactorsevaluateto returnEvaluationResult; removes lifecycle observer hooks; minor IO dispatching tweak.Written by Cursor Bugbot for commit 661ffa2. This will update automatically on new commits. Configure here.