Skip to content
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

Fixes #5572 : Softer redirection for learners who need to revisit earlier cards #5626

Open
wants to merge 28 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d819e70
Merge pull request #1 from oppia/develop
subhajitxyz Jul 23, 2024
c6f54ea
Merge branch 'oppia:develop' into develop
subhajitxyz Jul 27, 2024
d44015b
Merge remote-tracking branch 'upstream/develop' into develop
subhajitxyz Aug 17, 2024
d604bc2
Merge pull request #2 from oppia/develop
subhajitxyz Aug 26, 2024
cc0ddef
Merge pull request #3 from oppia/develop
subhajitxyz Aug 26, 2024
52c6bb1
Merge pull request #4 from oppia/develop
subhajitxyz Aug 29, 2024
773c810
Merge remote-tracking branch 'upstream/develop' into develop
subhajitxyz Sep 5, 2024
56af5ae
Merge pull request #5 from oppia/develop
subhajitxyz Sep 16, 2024
3883f70
Merge pull request #6 from oppia/develop
subhajitxyz Sep 27, 2024
10c8e6e
Fix #5404: Migrate away from onBackPressed for remaining activities (…
dattasneha Oct 3, 2024
5e140e9
Fix #5404: Migrate away from onBackPressed for RevisionCardActivity (…
dattasneha Oct 9, 2024
b4ad7a3
Merge branch 'oppia:develop' into develop
subhajitxyz Oct 11, 2024
238645d
Merge pull request #8 from oppia/develop
subhajitxyz Oct 12, 2024
5a546b9
Merge pull request #9 from oppia/develop
subhajitxyz Nov 3, 2024
b1ca8e1
Merge pull request #10 from oppia/develop
subhajitxyz Nov 19, 2024
ad7e380
Merge pull request #11 from oppia/develop
subhajitxyz Dec 11, 2024
b938a4c
Fix #5508: Skipping redundant code coverage and APK/AAB report commen…
Rd4dev Dec 12, 2024
fc2f932
Fix part of #4865: Use profileId in classroom activity and presenter …
tobioyelekan Dec 16, 2024
35f937b
Merge branch 'oppia:develop' into develop
subhajitxyz Dec 17, 2024
21b0986
Merge branch 'oppia:develop' into develop
subhajitxyz Dec 18, 2024
d891a5a
Merge branch 'oppia:develop' into develop
subhajitxyz Dec 21, 2024
7c12668
Merge branch 'oppia:develop' into develop
subhajitxyz Dec 23, 2024
040c157
Merge branch 'oppia:develop' into develop
subhajitxyz Dec 26, 2024
c5db6ea
Merge branch 'oppia:develop' into develop
subhajitxyz Dec 27, 2024
b031bd3
Add functionality for softer redirection
subhajitxyz Dec 27, 2024
ccb1874
Added missinig kdoc
subhajitxyz Dec 27, 2024
c45ebc2
update kdoc
subhajitxyz Dec 27, 2024
fe86e2f
initialize shouldRevisitEarlierCard in resumedeck
subhajitxyz Dec 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5238,6 +5238,52 @@ class StateFragmentTest {
}
}

@Test
fun testStateFragment_usesSofterRedirection_afterRevisitingEarlierCard() {
setUpTestWithLanguageSwitchingFeatureOff()
launchForExploration(RATIOS_EXPLORATION_ID_0, shouldSavePartialProgress = false).use {
startPlayingExploration()

playThroughRatioExplorationState1()
playThroughRatioExplorationState2()
playThroughRatioExplorationState3()
playThroughRatioExplorationState4()
playThroughRatioExplorationState5()
playThroughRatioExplorationState6()
playThroughRatioExplorationState7()
playThroughRatioExplorationState8()
playThroughRatioExplorationState9()
playThroughRatioExplorationState10()
playThroughRatioExplorationState11()
playThroughRatioExplorationState12()
playThroughRatioExplorationState13()
playThroughRatioExplorationState14()
playThroughRatioExplorationState15()

onView(
atPositionOnView(
recyclerViewId = R.id.selection_interaction_recyclerview,
position = 1,
targetViewId = R.id.multiple_choice_content_text_view
)
).perform(click())
clickSubmitAnswerButton()
clickContinueNavigationButton()

scrollToViewType(NEXT_NAVIGATION_BUTTON)
onView(withId(R.id.next_state_navigation_button)).check(matches(isDisplayed()))
onView(withId(R.id.feedback_text_view))
.check(matches(withText(containsString("Please continue."))))

clickNextNavigationButton()
clickNextNavigationButton()
clickNextNavigationButton()
clickNextNavigationButton()

verifySubmitAnswerButtonIsEnabled()
}
}

private fun playThroughRatioExplorationState1() {
clickContinueInteractionButton()
}
Expand Down Expand Up @@ -5311,6 +5357,12 @@ class StateFragmentTest {
clickContinueNavigationButton()
}

private fun playThroughRatioExplorationState15() {
typeTextInput("2:1")
clickSubmitAnswerButton()
clickContinueNavigationButton()
}

private fun addShadowMediaPlayerException(dataSource: Any, exception: Exception) {
val classLoader = StateFragmentTest::class.java.classLoader!!
val shadowMediaPlayerClass = classLoader.loadClass("org.robolectric.shadows.ShadowMediaPlayer")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,14 @@ class ExplorationProgressController @Inject constructor(
when {
answerOutcome.destinationCase == AnswerOutcome.DestinationCase.STATE_NAME -> {
endState()
// Determines if a revision is required for the user based on the answer outcome.
if (!answerOutcome.labelledAsCorrectAnswer &&
answerOutcome.feedback.contentId.contains("feedback", true)
) {
explorationProgress.stateDeck.turnOnRevisitEarlierCard(true)
} else {
explorationProgress.stateDeck.turnOnRevisitEarlierCard(false)
}
val newState = explorationProgress.stateGraph.getState(answerOutcome.stateName)
explorationProgress.stateDeck.pushState(
newState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class StateDeck constructor(
private val previousStates: MutableList<EphemeralState> = ArrayList()
private val currentDialogInteractions: MutableList<AnswerAndResponse> = ArrayList()
private var stateIndex: Int = 0
private var shouldRevisitEarlierCard: Boolean = false

/** Resets this deck to a new, specified initial [State]. */
fun resetDeck(initialState: State) {
Expand All @@ -46,6 +47,7 @@ class StateDeck constructor(
this.previousStates.addAll(previousStates)
this.currentDialogInteractions.addAll(currentDialogInteractions)
this.stateIndex = stateIndex
if (getStateIndexOfEarlierCard(pendingTopState.name) != null) shouldRevisitEarlierCard = true
}

/** Navigates to the previous state in the deck, or fails if this isn't possible. */
Expand All @@ -57,12 +59,19 @@ class StateDeck constructor(
/** Navigates to the next state in the deck, or fails if this isn't possible. */
fun navigateToNextState() {
check(!isCurrentStateTopOfDeck()) { "Cannot navigate to next state; at most recent state." }
val previousState = previousStates[stateIndex]
stateIndex++
if (!previousState.hasNextState) {
// Update the previous state to indicate that it has a next state now that its next state has
// actually been reated' by navigating to it.
previousStates[stateIndex - 1] = previousState.toBuilder().setHasNextState(true).build()

val revisionIdx = getStateIndexOfEarlierCard(pendingTopState.name)

if (revisionIdx != null && stateIndex == previousStates.size - 1 && shouldRevisitEarlierCard) {
handleRevisitEarlierCard(revisionIdx)
} else {
val previousState = previousStates[stateIndex]
stateIndex++
if (!previousState.hasNextState) {
// Update the previous state to indicate that it has a next state now that its next state has
// actually been created' by navigating to it.
previousStates[stateIndex - 1] = previousState.toBuilder().setHasNextState(true).build()
}
}
}

Expand Down Expand Up @@ -151,7 +160,9 @@ class StateDeck constructor(
.setContinueButtonAnimationTimestampMs(timestamp)
.setShowContinueButtonAnimation(!isContinueButtonAnimationSeen && isCurrentStateInitial())
.build()
currentDialogInteractions.clear()
if (!shouldRevisitEarlierCard || getStateIndexOfEarlierCard(state.name) == null) {
currentDialogInteractions.clear()
}
pendingTopState = state
}

Expand Down Expand Up @@ -251,4 +262,52 @@ class StateDeck constructor(
private fun isTopOfDeckTerminal(): Boolean {
return isTopOfDeckTerminalChecker(pendingTopState)
}

/**
* Handles revisiting an earlier card when the user clicks the continue button.
*
* This function adjusts the state to facilitate revisiting a previously viewed card.
* - Removes the last added ephemeral state from [previousStates], which was added when user
* submitted a wrong answer.
* - Updates [pendingTopState] to the current state with the stored [currentDialogInteractions],
* marking it as a pending state where the user needs to submit a correct answer.
* - Updates [stateIndex] to the provided [revisionIdx].
* - Turns off the revisit mode by setting [shouldRevisitEarlierCard] to `false`.
*
* @param revisionIdx the index of the state to revisit.
*/
private fun handleRevisitEarlierCard(revisionIdx: Int) {
val timestamp = previousStates[previousStates.size - 1].continueButtonAnimationTimestampMs
val showContinueButtonSeen =
previousStates[previousStates.size - 1].showContinueButtonAnimation
val currentState = previousStates[previousStates.size - 1].state

previousStates.removeAt(previousStates.size - 1)

pendingTopState = EphemeralState.newBuilder()
.setState(currentState)
.setHasPreviousState(!isCurrentStateInitial())
.setPendingState(PendingState.newBuilder().addAllWrongAnswer(currentDialogInteractions))
.setContinueButtonAnimationTimestampMs(timestamp)
.setShowContinueButtonAnimation(showContinueButtonSeen)
.build().state

stateIndex = revisionIdx
turnOnRevisitEarlierCard(false)
}

/** Returns [stateIndex] if state present on [previousStates] list. */
private fun getStateIndexOfEarlierCard(stateName: String): Int? {
for (i in previousStates.size - 1 downTo 0) {
if (previousStates[i].state.name == stateName) {
return i
}
}
return null
}

/** Sets whether the user should revisit an earlier card. */
fun turnOnRevisitEarlierCard(value: Boolean) {
shouldRevisitEarlierCard = value
}
}
Loading