-
Notifications
You must be signed in to change notification settings - Fork 528
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
Fix#4881: Build UI for the Android NPS Survey #4945
Conversation
Switched to use more descriptive naming.
Tested that overall gating logic works as expected.
… to an exploration started and stopped. These will be called by controllers as a way to get event-driven information in a lifecycle-safe way.
…lication:WIP Used dummy default values. Work needs to be done to pass the correct defaults or change the initialization.
Created a provider and added the module to the application components.
ExplorationProgressController.kt will fire an ExplorationProgressListener callback whenever an exploration is started or finished. TODO: pass the actual values to onExplorationSessionPaused()
This logic currently lives in the presenter, but we need it to move to the controller so that the UI does not have to make this decision, but rather observe and respond to a state sent by the controller.
We need to pass the correct instances of topic ID and profile ID, and move the implementation of ApplicationLifecycleListener to it's own class.
…CABLE_WONT_USE_OPPIA_ANYMORE 'NA' is a bit ambiguous, and the context is "won't use Oppia anymore". If they aren't using the app, they presumably wouldn't be filling out this survey.
For proto enums, we don't need to prefix-qualify each one since they'll be part of a generated Java enum -- except the first one since the unspecified constant should always repeat the enum name per proto convention.
Refactored to explain the ID format and the uniqueness guaranteed.
To convey that it is a multiple choice answer option.
Sounds more in line with the package location and purpose.
…to nps-survey-gating-logic
Leaving a proto list empty raises the potential ambiguity of an empty proto value, which can make it harder to reason about the model. By using oneof, we can better control the structure of the message and make it clearer what fields are relevant to each question type.
We can use this field to filter open-ended responses in analytics
Fix broken tests from refactor and add more UI test scenarios for backward navigation.
Also renamed survey_multiple_choice_item.xml due to a typo that made it difficult to search for the file.
Hi @seanlip, I have addressed all your comments on the PR, PTAL! I have also updated the PR description and the videos. |
This turned out to be a flaky test scenario, they are mostly passing. |
Unassigning @adhiamboperes since a re-review was requested. @adhiamboperes, please make sure you have addressed all review comments. Thanks! |
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.
Thanks @adhiamboperes! Just a few more inline comments. Additionally, for the new video:
- At 0:07 and 0:10 it seems that an incomplete question displays and then later the options appear. This is quite jarring. Can all parts of the question be loaded at once?
- Around 0:30 there is a brief flash of the textarea and then it jumps back to the NPS question. This seems like a bug?
app/src/main/java/org/oppia/android/app/application/BUILD.bazel
Outdated
Show resolved
Hide resolved
app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/MarketFitItemsViewModel.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/org/oppia/android/app/survey/surveyitemviewmodel/UserTypeItemsViewModel.kt
Show resolved
Hide resolved
I have looked into these:
This is only visible because I had recorded the video at an extremely low bitrate(4Mbps). In reality, I am not able to observe serious visual jank.
I rechecked and this happened between actual next and prev button clicks in quick succession. I cannot repro 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.
Thanks @adhiamboperes! LGTM.
Just one thing I noticed when watching the demo again -- the fourth bullet point in the second question should be "N/A - I don't use Oppia anymore". I.e. there is a missing "I".
Hi @adhiamboperes, this PR is ready to be merged. Please address any remaining comments prior to merging, and feel free to merge this PR once the CI checks pass and you're happy with it. Thanks! |
I am curious about this: from the mocks there is no I, but other PMF samples I have seen seem to have it. |
Yup, I think that was just a mistake, let's please add it. (Also in the future, if you see anything that looks "off" please do bring it up directly with the design/product team. Thanks!) |
Hey @adhiamboperes. I have reviewed the remaining test issue and that is now resolved. Everything is good now. |
Thanks @seanlip, @kkmurerwa @MohitGupta121! |
Explanation
Fixes #4881.
This is PR 3 of 6 that adds the UI for the NPS survey as well as completes the key user flows.
Layouts Introduced
Main Layouts
Technical decisions
I used the
BindableAdapter.MultiTypeBuilder
from within the fragment to populate the views. This allowed me to reuse the survey question layout while creating individual layouts for each answer option group.I created a viewmodel hierachy where:
SurveyAnswerItemViewModel
for representing the different types of different layouts that can exist for the recyclerView responsible for displaying these views.I used view binding to display all the strings and control navigation.
I also created custom views where using regular views proved to be complex to work with.
Custom Views
Navigation and Progress
I found it difficult to decouple the UI development work from the progress work, since the survey and the questions are generated dynaically so I needed to develop both in tandem.
The following changes pertain to the core survey logic:
SurveyController
Creates a survey with a list of questions.
SurveyProgressController
Tracks the non-persisted progress of a survey and is responsible for handling the survey session, managing the commands for navigating through and answer submission.
I opted to use a command pattern similar to what is used in the ExplorationProgressController.
Navigation
The survey can be viewed as a series of linear questions, which we will navigate through using methods defined in the SurveyQuestionDeck, modeled after the StateDeck. To start with, we will create an EphemeralSurveyQuestion which represents the question the user is currently viewing. It contains a reference to the next and previous questions if applicable, and a reference to whether it is the last question in the deck. The SurveyQuestionDeck will define the conditions for navigating to the previous and next questions as well as the exceptions thrown in case a navigation action is invalid.
A SurveyQuestionGraph class will be created to provide functionality for processing the behavior of a ‘next’ navigation action, which evaluates which question is shown next.
Survey Proto Changes:
UserTypeAnswer
enum so that they result into a properly indexed options list per the mocks.EphemeralSurveyQuestion
to have acurrent_question_index
and atotal_question_count
to make it easy to compute the survey progress.free_form_answer
to theSurveySelectedAnswer
message to hold the value of a free text response.SurveyQuestion optional_question = 3;
field to the survey so that we have a clear seperation between the mandatory and optional questions. This also supports the survey generation architecture.Bazel
Created
BUILD
definitions for all new classes and tests.Dagger
I changed approximately 150 test files to include
SurveyQuestionModule::class
, though I removed it in a subsequent refactor. When I removed the module and it's usages, it was not a very clean revert because some of the files were not properly formatted before, but they are now -- hence some test files here with only formatting changes.Usages, Future Work and Out of Scope
The domain layer of the survey is pretty much flexible to support any survey question, including should the current list be reordered or the number of questions changed. For example, if we wanted to show a subsequent survey on the same profile:
Show One Question Only
We would be able to show only one question if we only passed that one question name to the list in the
startSurveySession()
function. We would also need to setshowOptionalQuestion
to false, to disable adding the free-form question to the survey. This is otherwise created by default asshowOptionalQuestion
is always true otherwise.Further work would need to be done in the UI to to update the navigation button behaviour, i.e currently the button will show next instead of submit, and to ensure the submit answer functionality works to exit survey once the one answer has been submitted.
Show Two or More Questions
We could for example want to show only the NPS score question and the feedback question. We would need to pass in the NPS question's name as an input to the
startSurveySession()
function. The feedback question name never needs to be explicitly passed since we create a default (promoter) question and typically update the actual question based on the NPS score.The UI currently supports this scenario.
Add More Question Types
The domain layer would be able to handle any question types, but the UI will need to be updated to display this question by creating a new layout, and viewmodel for it, as well as updating the
SurveyAnswerItemViewModel
'sViewType
enum with this new viewType(s).A more longterm solution for a dynamic UI would be to create standardized survey questions and answer options, pretty much like state/exploration interactions.
Essential Checklist
For UI-specific PRs only
If your PR includes UI-related changes, then: