Is your feature request related to a problem? Please describe.
This project is two smaller user-facing improvements combined into a single project.
The first part is completing the work that began in #5388 to address #4652. The Android app supports users exiting a lesson early and resuming where they left off later. However, they are unable to monitor their progress through the lesson and this has come up repeatedly in user feedback. As mentioned in this discussion thread the Oppia web platform already supports annotating exploration states with a property for whether the card is a checkpoint. This is crucial because showing progress isn't as simple as counting the number of cards the user completed and representing that out of the total number of cards: explorations are dynamic. Users may go down diverging pathways through their lesson experience since the lesson may have a branch with multiple cards to review a concept that the user doesn't fully understand. Instead, we need to provide a means of showing progress that accounts for this dynamic structure using the checkpoints as key points.
Note that one aspect of this that's a bit challenging is that the exact mocks are not defined for how this screen should look.
The second part of this project is introducing support for study guides and worked examples, both of which the Oppia web platform now supports. In particular:
- Study guides are a change to the existing 'revision cards' in the app that introduce multiple sections with section headers.
- Worked examples are rich text components that are meant to provide a collapsible box with a "question" and "answer" that the user can view if they want to see an example.
In Android, we want to implement study guides in a way that looks similar to Oppia web (see this Figma project).
Worked examples, however, are quite challenging to implement in the way that Oppia web does because the app currently translates custom rich text components to Android Spannables (see HtmlParser), and interactive spannables are very difficult to implement without substantial accessibility problems. The team assumes that this can only be solved with a complete reworking of how HTML tags are handled which is outside the scope of this project. Instead, this project will require introducing a new in-app tag handler that inlines the worked example question and answer in a way that matches how lesson authors represented this before and in a way that's translatable and works for RTL languages like Arabic. Essentially, a worked example with the tag <oppia-noninteractive-workedexample question-with-value="&quot;&lt;pre&gt;&lt;p&gt;lorem ipsum&lt;/p&gt;&lt;/pre&gt;&quot;" answer-with-value="&quot;lorem ipsum&quot;"></oppia-noninteractive-workedexample> would be rendered into two lines with block spacing:
<p>
<strong>Question</strong>:
<br>
<pre><p>lorem ipsum</p></pre>
</p>
<p>
<string>Answer</strong>:
<br>
lorem ipsum
</p>
Describe the solution you'd like
Suggested Milestones:
-
Milestone 1:
- Introduction of three new feature flags: lesson progress visualization, study guides, and worked examples.
- Support for lesson progress visualization gated behind the feature flag.
- Support for the new study guides experience, gated behind the study guides feature flag.
- Completed QA testing for both features with all bugs triaged (see below for note on launch readiness).
-
Milestone 2:
- Support for worked examples, gated behind the worked examples feature flag.
- Completed QA testing for the worked examples feature (see below for note on launch readiness).
- Bugs fixed for all three features.
- Launch all three features.
Important note on launch readiness: we want this project to include fully launching all three features which means the milestones must be scheduled such that there's sufficient time for QA testing (which will require 2 weeks time), fixing bugs, and completing product reviews of the features early enough for the features to be completed before the next app release is cut. Exceptions cannot be made for late work here because the release process may be automated by this point in the summer (milestone 2).
Technical hints / guidance
- General changes overview:
- Since the new functionality is gated behind feature flags the easiest way to implement some of the features is to introduce entire new versions of the affected files. In other cases it's easier to just gate in single places. Specifically:
- Study guides will be easier to implement as an entirely new package (also partly because they're being renamed to 'Study Guides' from 'Revision Cards').
- Lesson progress and worked examples will be easier to gate, though worked examples are slightly tricky because they need to be completely ignored when the feature is off (which means introducing two different handlers).
- Test lessons will need to be updated. It's recommended to specifically update:
test_exp_id_2.textproto to have card_is_checkpoint set at a few points (at least 3) so that lesson progress can be demonstrated for the test lesson.
test_topic_id_0_1.textproto to include both worked examples and to use the new revision card structure.
- UI changes:
- A new package:
app/topic/studyguide which largely copies from app/topic/revisioncard except that it will use a RecyclerView and BindableAdapter with two model types: heading and content. Note that:
- New UI layout files will be introduced.
- Using Jetpack Compose is okay but not mandated. Jetpack Compose should not be used for worked examples or lesson progress.
- There may be new strings needed for the 'Study Guide' terminology (the app should match the web platform when the feature is enabled).
- Possibly multiple files under
app/player/state to support lesson progress, but in particular: StatePlayerRecyclerViewAssembler since this is the primary consumer of EphemeralState from ExplorationProgressController.
- A new feature will need to be added to the assembler to support lesson progress.
- This feature will only be enabled for explorations (via
StateFragmentPresenter.createRecyclerViewAssembler), not questions, and only when the flag is enabled.
- The current concept mock has the progress indicator next to the navigation buttons. This can only be done by modifying three different layouts:
continue_interaction_item.xml, continue_navigation_button_item.xml, and next_button_item.xml which should be hidden in each unless specifically enabled via the respective view models through StatePlayerRecyclerViewAssembler.
- Domain changes:
ExplorationProgressController in order to compute the current state progress and pass it through EpehemeralState. However, StateDeck is the utility actually responsible for calculating EphemeralState, and StateGraph will also be necessary for making this work.
StateDeck:
- Suggest adding a helper method to compute the checkpoint count at the current state. This should be as simple as counting the number of states with
card_is_checkpoint set plus 1 since the initial state is considered a checkpoint.
getCurrentEphemeralState will need to be updated to take a parameter for the total number of checkpoints that will be passed from ExplorationProgressController.
getCurrentEphemeralState should not populate the corresponding fields if the feature is disabled or if the provided count is null (which may happen--see below).
StateGraph:
- Suggest adding a new API property
checkpointCount: Int? which is lazy initialized by calling a new helper: fun computeCheckpointCount(): Int? which returns null if the exploration has zero states marked as card_is_checkpoint (indicating that it doesn't support checkpoints). Note that the lazy initialization is to memoize the value since it will not be trivial to compute and will be needed many times throughout playing an exploration (and isn't expected to change).
- For actual behavior:
computeCheckpointCount needs to perform a pathfind through the state graph from the initial state (whose name will need to be passed into StateGraph's constructor) to all terminal states (using the same terminal checker passed into StateDeck which will need to be passed into StateGraph's constructor). Specific details:
- The function should first check that there's exactly one terminal state. If there's more than one then log a warning and return null. This drastically simplifies the algorithm to a pathfind just between two points in the graph.
- From the starting to ending state follow the main linear path: this is done by going through the
AnswerGroups of each state one-by-one and following to the state corresponding to the answer marked using labelled_as_correct. There can be more than one answer marked as correct. All correct answers will eventually lead to the terminal state.
- This is a good application for Dijkstra's algorithm which should yield a single path even if there's more than one correct answer in a state.
- The single path that's found can then be walked to count all of the
card_is_checkpoint states and return that count plus 2 (for the initial and terminal states).
- Note: complex exploration arrangements including multiple correct answers in a single state going to diverging pathways that eventually meet back to the main path should be included in the tests for
StateGraph. These do not need to be represented in the test exploration.
- Note that the learner progress functionality must work with all of the following:
- Lesson checkpoint saving and restoring.
- Flashbacks.
- Navigating backward/forward in the state deck (i.e. the progress should count down if the user navigates back to a state previous to the current completed checkpoint count).
- Utility changes:
HtmlParser to support a new tag handler for worked examples.
- See
computeCustomTagHandlers and the other custom tag handlers for an idea on the implementation.
- Note that being able to parse arbitrary HTML inside a tag handler isn't currently supported. As such
CustomContentHtmlHandler.CustomTagHandler specifically handleTag and handleTagForContentDescription will need to be updated to take a new interface CustomHtmlParser which takes an html String and returns a Spannable. A default implementation of this interface can be provided via a call to CustomContentHtmlHandler.fromHtml.
- Model changes:
exploration.proto's EphemeralState should be updated to include new fields: CheckpointProgress checkpoint_progress which is not set if the functionality is disdabled. This new proto will need two fields: int32 completed_checkpoint_count and int32 total_checkpoint_count.
Describe alternatives you've considered
No response
Additional context
This is the high-level tracking issue corresponding to https://github.com/oppia/oppia/wiki/Google-Summer-of-Code-2026#41-support-for-lesson-progress-study-guides-and-worked-examples.
This issue has some child issues that are expected to be resolved as part of this project (due to their specific needs being met).
Finally, the following items must be addressed before this project can begin coding:
Is your feature request related to a problem? Please describe.
This project is two smaller user-facing improvements combined into a single project.
The first part is completing the work that began in #5388 to address #4652. The Android app supports users exiting a lesson early and resuming where they left off later. However, they are unable to monitor their progress through the lesson and this has come up repeatedly in user feedback. As mentioned in this discussion thread the Oppia web platform already supports annotating exploration states with a property for whether the card is a checkpoint. This is crucial because showing progress isn't as simple as counting the number of cards the user completed and representing that out of the total number of cards: explorations are dynamic. Users may go down diverging pathways through their lesson experience since the lesson may have a branch with multiple cards to review a concept that the user doesn't fully understand. Instead, we need to provide a means of showing progress that accounts for this dynamic structure using the checkpoints as key points.
Note that one aspect of this that's a bit challenging is that the exact mocks are not defined for how this screen should look.
The second part of this project is introducing support for study guides and worked examples, both of which the Oppia web platform now supports. In particular:
In Android, we want to implement study guides in a way that looks similar to Oppia web (see this Figma project).
Worked examples, however, are quite challenging to implement in the way that Oppia web does because the app currently translates custom rich text components to Android
Spannables (seeHtmlParser), and interactive spannables are very difficult to implement without substantial accessibility problems. The team assumes that this can only be solved with a complete reworking of how HTML tags are handled which is outside the scope of this project. Instead, this project will require introducing a new in-app tag handler that inlines the worked example question and answer in a way that matches how lesson authors represented this before and in a way that's translatable and works for RTL languages like Arabic. Essentially, a worked example with the tag<oppia-noninteractive-workedexample question-with-value="&quot;&lt;pre&gt;&lt;p&gt;lorem ipsum&lt;/p&gt;&lt;/pre&gt;&quot;" answer-with-value="&quot;lorem ipsum&quot;"></oppia-noninteractive-workedexample>would be rendered into two lines with block spacing:Describe the solution you'd like
Suggested Milestones:
Milestone 1:
Milestone 2:
Important note on launch readiness: we want this project to include fully launching all three features which means the milestones must be scheduled such that there's sufficient time for QA testing (which will require 2 weeks time), fixing bugs, and completing product reviews of the features early enough for the features to be completed before the next app release is cut. Exceptions cannot be made for late work here because the release process may be automated by this point in the summer (milestone 2).
Technical hints / guidance
test_exp_id_2.textprototo havecard_is_checkpointset at a few points (at least 3) so that lesson progress can be demonstrated for the test lesson.test_topic_id_0_1.textprototo include both worked examples and to use the new revision card structure.app/topic/studyguidewhich largely copies fromapp/topic/revisioncardexcept that it will use aRecyclerViewandBindableAdapterwith two model types: heading and content. Note that:app/player/stateto support lesson progress, but in particular:StatePlayerRecyclerViewAssemblersince this is the primary consumer ofEphemeralStatefromExplorationProgressController.StateFragmentPresenter.createRecyclerViewAssembler), not questions, and only when the flag is enabled.continue_interaction_item.xml,continue_navigation_button_item.xml, andnext_button_item.xmlwhich should be hidden in each unless specifically enabled via the respective view models throughStatePlayerRecyclerViewAssembler.ExplorationProgressControllerin order to compute the current state progress and pass it throughEpehemeralState. However,StateDeckis the utility actually responsible for calculatingEphemeralState, andStateGraphwill also be necessary for making this work.StateDeck:card_is_checkpointset plus 1 since the initial state is considered a checkpoint.getCurrentEphemeralStatewill need to be updated to take a parameter for the total number of checkpoints that will be passed fromExplorationProgressController.getCurrentEphemeralStateshould not populate the corresponding fields if the feature is disabled or if the provided count is null (which may happen--see below).StateGraph:checkpointCount: Int?which is lazy initialized by calling a new helper:fun computeCheckpointCount(): Int?which returnsnullif the exploration has zero states marked ascard_is_checkpoint(indicating that it doesn't support checkpoints). Note that the lazy initialization is to memoize the value since it will not be trivial to compute and will be needed many times throughout playing an exploration (and isn't expected to change).computeCheckpointCountneeds to perform a pathfind through the state graph from the initial state (whose name will need to be passed intoStateGraph's constructor) to all terminal states (using the same terminal checker passed intoStateDeckwhich will need to be passed intoStateGraph's constructor). Specific details:AnswerGroups of each state one-by-one and following to the state corresponding to the answer marked usinglabelled_as_correct. There can be more than one answer marked as correct. All correct answers will eventually lead to the terminal state.card_is_checkpointstates and return that count plus 2 (for the initial and terminal states).StateGraph. These do not need to be represented in the test exploration.HtmlParserto support a new tag handler for worked examples.computeCustomTagHandlersand the other custom tag handlers for an idea on the implementation.CustomContentHtmlHandler.CustomTagHandlerspecificallyhandleTagandhandleTagForContentDescriptionwill need to be updated to take a new interfaceCustomHtmlParserwhich takes an htmlStringand returns aSpannable. A default implementation of this interface can be provided via a call toCustomContentHtmlHandler.fromHtml.exploration.proto'sEphemeralStateshould be updated to include new fields:CheckpointProgress checkpoint_progresswhich is not set if the functionality is disdabled. This new proto will need two fields:int32 completed_checkpoint_countandint32 total_checkpoint_count.Describe alternatives you've considered
No response
Additional context
This is the high-level tracking issue corresponding to https://github.com/oppia/oppia/wiki/Google-Summer-of-Code-2026#41-support-for-lesson-progress-study-guides-and-worked-examples.
This issue has some child issues that are expected to be resolved as part of this project (due to their specific needs being met).
Finally, the following items must be addressed before this project can begin coding:
card_is_checkpointvia lesson pipeline #6103