Skip to content

Commit

Permalink
learner progress - state updater while playing through exploration le…
Browse files Browse the repository at this point in the history
…sson
  • Loading branch information
Rd4dev committed Apr 17, 2024
1 parent 3a06124 commit d31a993
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.app.utility.SplitScreenManager
import org.oppia.android.app.utility.lifecycle.LifecycleSafeTimerFactory
import org.oppia.android.databinding.StateFragmentBinding
import org.oppia.android.domain.exploration.ExplorationRetriever
import org.oppia.android.domain.exploration.ExplorationProgressController
import org.oppia.android.domain.oppialogger.OppiaLogger
import org.oppia.android.domain.survey.SurveyGatingController
Expand All @@ -54,6 +55,7 @@ import org.oppia.android.util.gcsresource.DefaultResourceBucketName
import org.oppia.android.util.parser.html.ExplorationHtmlParserEntityType
import org.oppia.android.util.system.OppiaClock
import javax.inject.Inject
import kotlinx.coroutines.runBlocking

const val STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY =
"StateFragmentPresenter.state_fragment_profile_id"
Expand All @@ -71,6 +73,7 @@ class StateFragmentPresenter @Inject constructor(
private val fragment: Fragment,
private val context: Context,
private val lifecycleSafeTimerFactory: LifecycleSafeTimerFactory,
private val explorationRetriever: ExplorationRetriever,
private val explorationProgressController: ExplorationProgressController,
private val storyProgressController: StoryProgressController,
private val oppiaLogger: OppiaLogger,
Expand Down Expand Up @@ -99,6 +102,7 @@ class StateFragmentPresenter @Inject constructor(
private var forceAnnouncedForHintsBar = false

private lateinit var recyclerViewAssembler: StatePlayerRecyclerViewAssembler
private lateinit var explorationStateOrderData: Pair<Int, MutableMap<String, Int>>
private val ephemeralStateLiveData: LiveData<AsyncResult<EphemeralState>> by lazy {
explorationProgressController.getCurrentState().toLiveData()
}
Expand All @@ -124,6 +128,9 @@ class StateFragmentPresenter @Inject constructor(
container,
/* attachToRoot= */ false
)

explorationStateOrderData = fetchExplorationStateOrder(explorationId)

recyclerViewAssembler = createRecyclerViewAssembler(
assemblerBuilderFactory.create(resourceBucketName, entityType, profileId),
binding.congratulationsTextView,
Expand Down Expand Up @@ -294,6 +301,18 @@ class StateFragmentPresenter @Inject constructor(
)
}

private fun fetchExplorationStateOrder(explorationId: String): Pair<Int, MutableMap<String, Int>> {
var explorationStateOrderResult: Pair<Int, MutableMap<String, Int>>
runBlocking {
explorationStateOrderResult = loadExplorationStateOrderAsync(explorationId)
}
return explorationStateOrderResult
}

private suspend fun loadExplorationStateOrderAsync(explorationId: String): Pair<Int, MutableMap<String, Int>> {
return explorationRetriever.loadExplorationPosition(explorationId)
}

private fun processEphemeralStateResult(result: AsyncResult<EphemeralState>) {
when (result) {
is AsyncResult.Failure ->
Expand All @@ -320,6 +339,8 @@ class StateFragmentPresenter @Inject constructor(
currentState = ephemeralState.state
currentStateName = ephemeralState.state.name

updateStateProgress(ephemeralState.state)

showOrHideAudioByState(ephemeralState.state)

val dataPair = recyclerViewAssembler.compute(
Expand All @@ -341,6 +362,27 @@ class StateFragmentPresenter @Inject constructor(
}
}

private fun updateStateProgress(state: State) {
val currentStateName = state.name
val currentPosition = getPositionOfState(currentStateName, explorationStateOrderData)
val totalStates = explorationStateOrderData.first

var stateProgress = calculateStateProgress(currentPosition, totalStates)
viewModel.setStateProgress(stateProgress)
}

private fun getPositionOfState(currentStateName: String?, explorationStateOrderData: Pair<Int, MutableMap<String, Int>>): Int? {
return explorationStateOrderData.second[currentStateName]
}

private fun calculateStateProgress(currentPosition: Int?, totalStates: Int): Int {
var calculatedStateProgress : Double = 0.0
if (currentPosition != null) {
calculatedStateProgress = ((currentPosition.toDouble() / totalStates) * 100)
}
return calculatedStateProgress.toInt()
}

/** Subscribes to the result of requesting to show a hint or solution. */
private fun subscribeToHintSolution(resultDataProvider: DataProvider<Any?>) {
resultDataProvider.toLiveData().observe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class StateViewModel @Inject constructor(
val itemList: ObservableList<StateItemViewModel> = ObservableArrayList()
val rightItemList: ObservableList<StateItemViewModel> = ObservableArrayList()

var stateProgress = ObservableField(0)

val isSplitView = ObservableField(false)
val centerGuidelinePercentage = ObservableField(0.5f)

Expand Down Expand Up @@ -83,6 +85,10 @@ class StateViewModel @Inject constructor(
this.profileId = profileId
}

fun setStateProgress(progress: Int) {
stateProgress.set(progress)
}

fun setAudioBarVisibility(audioBarVisible: Boolean) {
isAudioBarVisible.set(audioBarVisible)
}
Expand Down
20 changes: 18 additions & 2 deletions app/src/main/res/layout/state_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<ProgressBar
android:id="@+id/state_progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="0dp"
android:layout_height="12dp"
android:max="100"
android:progress="@{viewModel.stateProgress}"
android:progressDrawable="@drawable/progress_bar"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/state_recycler_view"
android:layout_width="0dp"
Expand All @@ -39,7 +55,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/center_guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@id/state_progress_bar" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/extra_interaction_recycler_view"
Expand All @@ -56,7 +72,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/center_guideline"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@id/state_progress_bar" />

<androidx.constraintlayout.widget.Guideline
android:id="@+id/center_guideline"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ import org.oppia.android.app.model.Exploration
interface ExplorationRetriever {
/** Loads and returns an exploration for the specified exploration ID, or fails. */
suspend fun loadExploration(explorationId: String): Exploration
/** Loads and returns Pair of Total number of states for the specified exploration ID
* and a Mutable Map of State Names and their position in the exploration list
*/
suspend fun loadExplorationPosition(explorationId: String): Pair<Int, MutableMap<String, Int>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ class ExplorationRetrieverImpl @Inject constructor(
}
}

override suspend fun loadExplorationPosition(explorationId: String): Pair<Int, MutableMap<String, Int>> {
val explorationObject = jsonAssetRetriever.loadJsonFromAsset("$explorationId.json")
return parseJsonObjectToCalculatePosition(explorationObject!!)
}

// Returns an exploration given an assetName
private fun loadExplorationFromAsset(explorationObject: JSONObject): Exploration {
val innerExplorationObject = explorationObject.getJSONObject("exploration")
Expand Down Expand Up @@ -68,4 +73,41 @@ class ExplorationRetrieverImpl @Inject constructor(
}
return statesMap
}

private fun parseJsonObjectToCalculatePosition(explorationObject: JSONObject): Pair<Int, MutableMap<String, Int>> {
val states = explorationObject.getJSONObject("exploration").getJSONObject("states")
val initialState = explorationObject.getJSONObject("exploration").getString("init_state_name")

val statePosition = mutableMapOf<String, Int>()
var position = 0

// Function to recursively traverse the states and set their positions
fun getStatePosition(stateName: String) {
if (!statePosition.containsKey(stateName)) {
statePosition[stateName] = ++position

val state = states.getJSONObject(stateName)
val interaction = state.getJSONObject("interaction")

if (interaction.has("default_outcome") && !interaction.isNull("default_outcome")) {
val defaultOutcome = interaction.getJSONObject("default_outcome")
val dest = defaultOutcome.getString("dest")
getStatePosition(dest)
}

if (interaction.has("answer_groups") && interaction.getJSONArray("answer_groups").length() > 0) {
val answerGroups = interaction.getJSONArray("answer_groups")
for (i in answerGroups.length() - 1 downTo 0) {
val outcome = answerGroups.getJSONObject(i).getJSONObject("outcome")
val dest = outcome.getString("dest")
getStatePosition(dest)
}
}
}
}

getStatePosition(initialState)
return position to statePosition

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class FakeExplorationRetriever @Inject constructor(
return productionImpl.loadExploration(expIdToLoad)
}

override suspend fun loadExplorationPosition(explorationId: String): Pair<Int, MutableMap<String, Int>> {
val expIdToLoad = explorationProxies[explorationId] ?: explorationId
return productionImpl.loadExplorationPosition(expIdToLoad)
}

/**
* Sets the exploration ID that should be loaded in place of [expIdToLoad] on all future calls to
* [loadExploration].
Expand Down

0 comments on commit d31a993

Please sign in to comment.