From 46375afdd0aa247e8fb4b038a02e791a55ee7d47 Mon Sep 17 00:00:00 2001 From: Rd Date: Sun, 22 Sep 2024 14:51:51 +0530 Subject: [PATCH 01/21] Fix #1468: Retain Drag and Drop state after an incorrect answer --- .../DragAndDropSortInteractionViewModel.kt | 107 ++++++++++++++---- 1 file changed, 87 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 11d87f4bed0..6a96173fbbd 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -1,5 +1,6 @@ package org.oppia.android.app.player.state.itemviewmodel +import android.util.Log import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField @@ -24,8 +25,11 @@ import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.recyclerview.OnDragEndedListener import org.oppia.android.app.recyclerview.OnItemDragListener import org.oppia.android.app.translation.AppLanguageResourceHandler +import org.oppia.android.domain.exploration.ExplorationProgressController import org.oppia.android.domain.translation.TranslationController +import org.oppia.android.util.data.AsyncResult import javax.inject.Inject +import kotlinx.coroutines.runBlocking /** Represents the type of errors that can be thrown by drag and drop sort interaction. */ enum class DragAndDropSortInteractionError(@StringRes private var error: Int?) { @@ -49,11 +53,13 @@ class DragAndDropSortInteractionViewModel private constructor( private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, - userAnswerState: UserAnswerState + userAnswerState: UserAnswerState, + explorationProgressController: ExplorationProgressController ) : StateItemViewModel(ViewType.DRAG_DROP_SORT_INTERACTION), InteractionAnswerHandler, OnItemDragListener, OnDragEndedListener { + private val allowMultipleItemsInSamePosition: Boolean by lazy { interaction.customizationArgsMap["allowMultipleItemsInSamePosition"]?.boolValue ?: false } @@ -75,7 +81,13 @@ class DragAndDropSortInteractionViewModel private constructor( private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR private val _originalChoiceItems: MutableList = - computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) + computeOriginalChoiceItems( + contentIdHtmlMap, + choiceSubtitledHtmls, + this, + resourceHandler, + explorationProgressController + ) private val _choiceItems = computeSelectedChoiceItems( contentIdHtmlMap, @@ -252,7 +264,8 @@ class DragAndDropSortInteractionViewModel private constructor( /** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */ class FactoryImpl @Inject constructor( private val resourceHandler: AppLanguageResourceHandler, - private val translationController: TranslationController + private val translationController: TranslationController, + private val explorationProgressController: ExplorationProgressController ) : InteractionItemFactory { override fun create( entityId: String, @@ -275,7 +288,8 @@ class DragAndDropSortInteractionViewModel private constructor( writtenTranslationContext, resourceHandler, translationController, - userAnswerState + userAnswerState, + explorationProgressController ) } } @@ -301,24 +315,77 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap: Map, choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler + resourceHandler: AppLanguageResourceHandler, + explorationProgressController: ExplorationProgressController ): MutableList { - return choiceStrings.mapIndexed { index, subtitledHtml -> - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = subtitledHtml.contentId - } + return runBlocking { + when(val result = explorationProgressController.getCurrentState().retrieveData()) { + is AsyncResult.Success -> { + val ephemeralState = result.value + val wrongAnswerList = ephemeralState.pendingState.wrongAnswerList + + choiceStrings.mapIndexed { index, subtitledHtml -> + val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() + ?.userAnswer + ?.answer + ?.listOfSetsOfTranslatableHtmlContentIds + ?.contentIdListsList + ?.getOrNull(index) + ?.contentIdsList + ?.firstOrNull() + ?.contentId + + val contentHtmlFromWrongAnswer = wrongAnswerList?.lastOrNull() + ?.userAnswer + ?.listOfHtmlAnswers + ?.setOfHtmlStringsList + ?.get(index) + ?.htmlList + ?.firstOrNull() + + val updatedContentIdMap = mapOf( + contentIdFromWrongAnswer to contentHtmlFromWrongAnswer + ).filterKeys { it != null } + .filterValues { it != null } + .mapKeys { it.key as String } + .mapValues { it.value as String } + + DragDropInteractionContentViewModel( + contentIdHtmlMap = updatedContentIdMap.ifEmpty { + contentIdHtmlMap + }, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = contentIdFromWrongAnswer ?: subtitledHtml.contentId + } + ) + }.build(), + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() + } + else -> choiceStrings.mapIndexed { index, subtitledHtml -> + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = subtitledHtml.contentId + } + ) + }.build(), + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler ) - }.build(), - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() + }.toMutableList() + } + } } } From 541c0aea5f26d33da5d13f3cc139c401a6effd10 Mon Sep 17 00:00:00 2001 From: Rd Date: Sun, 22 Sep 2024 16:11:47 +0530 Subject: [PATCH 02/21] Removed unused import Yet to figure out if runBlocking is the suitable approach and then continue with figuring out the tests --- .../state/itemviewmodel/DragAndDropSortInteractionViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 6a96173fbbd..373867a0834 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -1,6 +1,5 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField From 030789246484c456703a7cce5066a82381d3c26d Mon Sep 17 00:00:00 2001 From: Rd Date: Sun, 22 Sep 2024 16:18:24 +0530 Subject: [PATCH 03/21] Fix Ktlint issues --- .../itemviewmodel/DragAndDropSortInteractionViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 373867a0834..2121b39c4ff 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.runBlocking import org.oppia.android.R import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction @@ -28,7 +29,6 @@ import org.oppia.android.domain.exploration.ExplorationProgressController import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.data.AsyncResult import javax.inject.Inject -import kotlinx.coroutines.runBlocking /** Represents the type of errors that can be thrown by drag and drop sort interaction. */ enum class DragAndDropSortInteractionError(@StringRes private var error: Int?) { @@ -318,7 +318,7 @@ class DragAndDropSortInteractionViewModel private constructor( explorationProgressController: ExplorationProgressController ): MutableList { return runBlocking { - when(val result = explorationProgressController.getCurrentState().retrieveData()) { + when (val result = explorationProgressController.getCurrentState().retrieveData()) { is AsyncResult.Success -> { val ephemeralState = result.value val wrongAnswerList = ephemeralState.pendingState.wrongAnswerList From a09bbf41e9587bcd94ed6bc5e3c6f048e61acc76 Mon Sep 17 00:00:00 2001 From: Rd Date: Sat, 28 Sep 2024 20:26:44 +0530 Subject: [PATCH 04/21] Updating the UI via live data - reset works; the empty input error recognition fails The implementation changes the initial value, so the original value is not equal to the or will never be equal to the choice items (other than the actual original reordering) since it is continuing from the retain state. So either this is the way to go or should figure out a way to set original item as retained value (which I doubt if could be properly synced as it is asynchronous and the first return is a empty list). --- .../DragAndDropSortInteractionViewModel.kt | 186 +++++++++--------- 1 file changed, 96 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 2121b39c4ff..6d4a6a7e773 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -3,10 +3,13 @@ package org.oppia.android.app.player.state.itemviewmodel import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField +import androidx.fragment.app.Fragment +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.RecyclerView -import kotlinx.coroutines.runBlocking import org.oppia.android.R import org.oppia.android.app.model.AnswerErrorCategory +import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -28,6 +31,7 @@ import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.domain.exploration.ExplorationProgressController import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.data.AsyncResult +import org.oppia.android.util.data.DataProviders.Companion.toLiveData import javax.inject.Inject /** Represents the type of errors that can be thrown by drag and drop sort interaction. */ @@ -53,7 +57,8 @@ class DragAndDropSortInteractionViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, userAnswerState: UserAnswerState, - explorationProgressController: ExplorationProgressController + private val fragment: Fragment, + private val explorationProgressController: ExplorationProgressController ) : StateItemViewModel(ViewType.DRAG_DROP_SORT_INTERACTION), InteractionAnswerHandler, OnItemDragListener, @@ -80,28 +85,26 @@ class DragAndDropSortInteractionViewModel private constructor( private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR private val _originalChoiceItems: MutableList = - computeOriginalChoiceItems( - contentIdHtmlMap, - choiceSubtitledHtmls, - this, - resourceHandler, - explorationProgressController - ) + computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) - private val _choiceItems = computeSelectedChoiceItems( + private var _choiceItems = computeSelectedChoiceItems( contentIdHtmlMap, choiceSubtitledHtmls, this, - resourceHandler, - userAnswerState + resourceHandler ) - val choiceItems: List = _choiceItems + + private val _choiceItemsLiveData = MutableLiveData>() + + val choiceItems: LiveData> = _choiceItemsLiveData private var pendingAnswerError: String? = null private val isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") init { + _choiceItemsLiveData.value = _choiceItems + val callback: Observable.OnPropertyChangedCallback = object : Observable.OnPropertyChangedCallback() { override fun onPropertyChanged(sender: Observable, propertyId: Int) { @@ -136,6 +139,7 @@ class DragAndDropSortInteractionViewModel private constructor( _choiceItems[indexFrom].itemIndex = indexFrom _choiceItems[indexTo].itemIndex = indexTo } + adapter.notifyItemMoved(indexFrom, indexTo) } @@ -222,6 +226,7 @@ class DragAndDropSortInteractionViewModel private constructor( dragDropInteractionContentViewModel.itemIndex = index dragDropInteractionContentViewModel.listSize = _choiceItems.size } + // to update the content of grouped item (adapter as BindableAdapter<*>).setDataUnchecked(_choiceItems) } @@ -249,6 +254,7 @@ class DragAndDropSortInteractionViewModel private constructor( dragDropInteractionContentViewModel.itemIndex = index dragDropInteractionContentViewModel.listSize = _choiceItems.size } + // to update the list (adapter as BindableAdapter<*>).setDataUnchecked(_choiceItems) } @@ -264,6 +270,7 @@ class DragAndDropSortInteractionViewModel private constructor( class FactoryImpl @Inject constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, + private val fragment: Fragment, private val explorationProgressController: ExplorationProgressController ) : InteractionItemFactory { override fun create( @@ -288,6 +295,7 @@ class DragAndDropSortInteractionViewModel private constructor( resourceHandler, translationController, userAnswerState, + fragment, explorationProgressController ) } @@ -314,66 +322,81 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap: Map, choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler, - explorationProgressController: ExplorationProgressController + resourceHandler: AppLanguageResourceHandler + ): MutableList { + return choiceStrings.mapIndexed { index, subtitledHtml -> + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = subtitledHtml.contentId + } + ) + }.build(), + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() + } + } + + private fun computeSelectedChoiceItems( + contentIdHtmlMap: Map, + choiceStrings: List, + dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, + resourceHandler: AppLanguageResourceHandler + ): MutableList { + val ephemeralStateLiveData: LiveData> by lazy { + explorationProgressController.getCurrentState().toLiveData() + } + + fun processEphemeralStateResult( + result: AsyncResult ): MutableList { - return runBlocking { - when (val result = explorationProgressController.getCurrentState().retrieveData()) { - is AsyncResult.Success -> { - val ephemeralState = result.value - val wrongAnswerList = ephemeralState.pendingState.wrongAnswerList - - choiceStrings.mapIndexed { index, subtitledHtml -> - val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() - ?.userAnswer - ?.answer - ?.listOfSetsOfTranslatableHtmlContentIds - ?.contentIdListsList - ?.getOrNull(index) - ?.contentIdsList - ?.firstOrNull() - ?.contentId - - val contentHtmlFromWrongAnswer = wrongAnswerList?.lastOrNull() - ?.userAnswer - ?.listOfHtmlAnswers - ?.setOfHtmlStringsList - ?.get(index) - ?.htmlList - ?.firstOrNull() - - val updatedContentIdMap = mapOf( - contentIdFromWrongAnswer to contentHtmlFromWrongAnswer - ).filterKeys { it != null } - .filterValues { it != null } - .mapKeys { it.key as String } - .mapValues { it.value as String } - - DragDropInteractionContentViewModel( - contentIdHtmlMap = updatedContentIdMap.ifEmpty { - contentIdHtmlMap - }, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = contentIdFromWrongAnswer ?: subtitledHtml.contentId - } - ) - }.build(), - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() - } - else -> choiceStrings.mapIndexed { index, subtitledHtml -> + return when (result) { + is AsyncResult.Failure -> mutableListOf() + is AsyncResult.Pending -> mutableListOf() + is AsyncResult.Success -> { + val state = result.value + val wrongAnswerList = state.pendingState.wrongAnswerList + + choiceStrings.mapIndexed { index, subtitledHtml -> + val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() + ?.userAnswer + ?.answer + ?.listOfSetsOfTranslatableHtmlContentIds + ?.contentIdListsList + ?.getOrNull(index) + ?.contentIdsList + ?.firstOrNull() + ?.contentId + + val contentHtmlFromWrongAnswer = wrongAnswerList?.lastOrNull() + ?.userAnswer + ?.listOfHtmlAnswers + ?.setOfHtmlStringsList + ?.get(index) + ?.htmlList + ?.firstOrNull() + + val updatedContentIdMap = mapOf( + contentIdFromWrongAnswer to contentHtmlFromWrongAnswer + ).filterKeys { it != null } + .filterValues { it != null } + .mapKeys { it.key as String } + .mapValues { it.value as String } + DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, + contentIdHtmlMap = updatedContentIdMap.ifEmpty { + contentIdHtmlMap + }, htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { addContentIds( TranslatableHtmlContentId.newBuilder().apply { - contentId = subtitledHtml.contentId + contentId = contentIdFromWrongAnswer ?: subtitledHtml.contentId } ) }.build(), @@ -386,29 +409,12 @@ class DragAndDropSortInteractionViewModel private constructor( } } } - } - private fun computeSelectedChoiceItems( - contentIdHtmlMap: Map, - choiceStrings: List, - dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler, - userAnswerState: UserAnswerState - ): MutableList { - return if (userAnswerState.listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount == 0) { - _originalChoiceItems.toMutableList() - } else { - userAnswerState.listOfSetsOfTranslatableHtmlContentIds.contentIdListsList - .mapIndexed { index, contentId -> - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = contentId, - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() + ephemeralStateLiveData.observe(fragment) { result -> + _choiceItems = processEphemeralStateResult(result) + _choiceItemsLiveData.value = _choiceItems } + + return _choiceItems?.takeIf { it.isNotEmpty() } ?: _originalChoiceItems.toMutableList() } } From 8e093cde90838882c40228491236f53b11d0a82f Mon Sep 17 00:00:00 2001 From: Rd Date: Sat, 28 Sep 2024 21:18:25 +0530 Subject: [PATCH 05/21] Updated the original choice items with a new instance of the choice Items This works well to check for the get Submit Time Error checks with the updated ephemeral state value as needed now the only step I is to make sure the _choiceItems is handled properly and also doubt if originalItems would even need any separate functionality. --- .../DragAndDropSortInteractionViewModel.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 6d4a6a7e773..e3b4e7dadd2 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -84,14 +84,15 @@ class DragAndDropSortInteractionViewModel private constructor( private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR - private val _originalChoiceItems: MutableList = + private var _originalChoiceItems: MutableList = computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) private var _choiceItems = computeSelectedChoiceItems( contentIdHtmlMap, choiceSubtitledHtmls, this, - resourceHandler + resourceHandler, + userAnswerState ) private val _choiceItemsLiveData = MutableLiveData>() @@ -347,7 +348,8 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap: Map, choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler + resourceHandler: AppLanguageResourceHandler, + userAnswerState: UserAnswerState ): MutableList { val ephemeralStateLiveData: LiveData> by lazy { explorationProgressController.getCurrentState().toLiveData() @@ -413,8 +415,11 @@ class DragAndDropSortInteractionViewModel private constructor( ephemeralStateLiveData.observe(fragment) { result -> _choiceItems = processEphemeralStateResult(result) _choiceItemsLiveData.value = _choiceItems + _originalChoiceItems = _choiceItems.toMutableList() } return _choiceItems?.takeIf { it.isNotEmpty() } ?: _originalChoiceItems.toMutableList() + +// _originalChoiceItems.toMutableList() } } From 090c244c3375fe6b3c2e954de055a68c311ca312 Mon Sep 17 00:00:00 2001 From: Rd Date: Sat, 28 Sep 2024 22:15:23 +0530 Subject: [PATCH 06/21] Initializing the _choiceItems to a empty list to never have a null reference The original choice item list would be still required as it is required as a fallback value for the _choiceItems. Running the tests resulted in 2 failures and they need to be addressed. --- .../DragAndDropSortInteractionViewModel.kt | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index e3b4e7dadd2..d9a5a85ebc5 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -87,16 +87,8 @@ class DragAndDropSortInteractionViewModel private constructor( private var _originalChoiceItems: MutableList = computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) - private var _choiceItems = computeSelectedChoiceItems( - contentIdHtmlMap, - choiceSubtitledHtmls, - this, - resourceHandler, - userAnswerState - ) - + private var _choiceItems: MutableList = mutableListOf() private val _choiceItemsLiveData = MutableLiveData>() - val choiceItems: LiveData> = _choiceItemsLiveData private var pendingAnswerError: String? = null @@ -104,6 +96,13 @@ class DragAndDropSortInteractionViewModel private constructor( var errorMessage = ObservableField("") init { + _choiceItems = computeSelectedChoiceItems( + contentIdHtmlMap, + choiceSubtitledHtmls, + this, + resourceHandler + ) + _choiceItemsLiveData.value = _choiceItems val callback: Observable.OnPropertyChangedCallback = @@ -348,8 +347,7 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap: Map, choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler, - userAnswerState: UserAnswerState + resourceHandler: AppLanguageResourceHandler ): MutableList { val ephemeralStateLiveData: LiveData> by lazy { explorationProgressController.getCurrentState().toLiveData() @@ -418,8 +416,7 @@ class DragAndDropSortInteractionViewModel private constructor( _originalChoiceItems = _choiceItems.toMutableList() } - return _choiceItems?.takeIf { it.isNotEmpty() } ?: _originalChoiceItems.toMutableList() - -// _originalChoiceItems.toMutableList() + return _choiceItems.takeIf { it.isNotEmpty() } + ?: _originalChoiceItems.toMutableList() } } From 94e86f4a5e99058e1e62b03c931b7c51ff276c28 Mon Sep 17 00:00:00 2001 From: Rd Date: Sun, 29 Sep 2024 06:54:16 +0530 Subject: [PATCH 07/21] Debugging save lobby before swiching to test in base branch --- .../DragAndDropSortInteractionViewModel.kt | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index d9a5a85ebc5..745081e6e3c 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -1,5 +1,6 @@ package org.oppia.android.app.player.state.itemviewmodel +import android.util.Log import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField @@ -100,7 +101,8 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap, choiceSubtitledHtmls, this, - resourceHandler + resourceHandler, + userAnswerState ) _choiceItemsLiveData.value = _choiceItems @@ -139,7 +141,6 @@ class DragAndDropSortInteractionViewModel private constructor( _choiceItems[indexFrom].itemIndex = indexFrom _choiceItems[indexTo].itemIndex = indexTo } - adapter.notifyItemMoved(indexFrom, indexTo) } @@ -226,7 +227,6 @@ class DragAndDropSortInteractionViewModel private constructor( dragDropInteractionContentViewModel.itemIndex = index dragDropInteractionContentViewModel.listSize = _choiceItems.size } - // to update the content of grouped item (adapter as BindableAdapter<*>).setDataUnchecked(_choiceItems) } @@ -254,7 +254,6 @@ class DragAndDropSortInteractionViewModel private constructor( dragDropInteractionContentViewModel.itemIndex = index dragDropInteractionContentViewModel.listSize = _choiceItems.size } - // to update the list (adapter as BindableAdapter<*>).setDataUnchecked(_choiceItems) } @@ -347,7 +346,8 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap: Map, choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler + resourceHandler: AppLanguageResourceHandler, + userAnswerState: UserAnswerState ): MutableList { val ephemeralStateLiveData: LiveData> by lazy { explorationProgressController.getCurrentState().toLiveData() @@ -362,8 +362,22 @@ class DragAndDropSortInteractionViewModel private constructor( is AsyncResult.Success -> { val state = result.value val wrongAnswerList = state.pendingState.wrongAnswerList + val wrongAnswerListCount = wrongAnswerList.lastOrNull() + ?.userAnswer?.listOfHtmlAnswers?.setOfHtmlStringsCount + + wrongAnswerList.mapIndexed { index, answerAndResponse -> + Log.d("useranswerstring", "processEphemeralStateResult: wrong list index - $index") + Log.d("useranswerstring", "processEphemeralStateResult: wrong list ar - $answerAndResponse") + } + + Log.d("useranswerstring", "processEphemeralStateResult: choice strings - $choiceStrings") + Log.d("useranswerstring", "processEphemeralStateResult: choice items - $_choiceItems") + Log.d("useranswerstring", "processEphemeralStateResult: user answer list - ${userAnswerState.listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount}") + Log.d("useranswerstring", "processEphemeralStateResult: user answer list count - ${wrongAnswerList.lastOrNull()?.userAnswer?.listOfHtmlAnswers?.setOfHtmlStringsCount}") choiceStrings.mapIndexed { index, subtitledHtml -> + Log.d("useranswerstring", "processEphemeralStateResult: chs index - $index") + Log.d("useranswerstring", "processEphemeralStateResult: chs sbh - $subtitledHtml") val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() ?.userAnswer ?.answer From 43a73e80ec1bc0ab0052a88476e18c80359cdcb9 Mon Sep 17 00:00:00 2001 From: Rd Date: Sun, 29 Sep 2024 08:12:10 +0530 Subject: [PATCH 08/21] Temporary Fix to handle grouping - resets (does not retain) A way to handle the grouping of elements with the wrongAnswerList is yet to be handled. Since choiceString points to the original size and list it does not align with the wrongAnswerList and also the wrongAnswerList and updateList would need a different handling - (to be figured out) --- .../DragAndDropSortInteractionViewModel.kt | 107 ++++++++---------- 1 file changed, 48 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 745081e6e3c..b5bbd30b7e7 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -1,6 +1,5 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField @@ -101,8 +100,7 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap, choiceSubtitledHtmls, this, - resourceHandler, - userAnswerState + resourceHandler ) _choiceItemsLiveData.value = _choiceItems @@ -346,8 +344,7 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap: Map, choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler, - userAnswerState: UserAnswerState + resourceHandler: AppLanguageResourceHandler ): MutableList { val ephemeralStateLiveData: LiveData> by lazy { explorationProgressController.getCurrentState().toLiveData() @@ -365,61 +362,53 @@ class DragAndDropSortInteractionViewModel private constructor( val wrongAnswerListCount = wrongAnswerList.lastOrNull() ?.userAnswer?.listOfHtmlAnswers?.setOfHtmlStringsCount - wrongAnswerList.mapIndexed { index, answerAndResponse -> - Log.d("useranswerstring", "processEphemeralStateResult: wrong list index - $index") - Log.d("useranswerstring", "processEphemeralStateResult: wrong list ar - $answerAndResponse") + if (choiceStrings.size == wrongAnswerListCount) { + choiceStrings.mapIndexed { index, subtitledHtml -> + val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() + ?.userAnswer + ?.answer + ?.listOfSetsOfTranslatableHtmlContentIds + ?.contentIdListsList + ?.getOrNull(index) + ?.contentIdsList + ?.firstOrNull() + ?.contentId + + val contentHtmlFromWrongAnswer = wrongAnswerList?.lastOrNull() + ?.userAnswer + ?.listOfHtmlAnswers + ?.setOfHtmlStringsList + ?.get(index) + ?.htmlList + ?.firstOrNull() + + val updatedContentIdMap = mapOf( + contentIdFromWrongAnswer to contentHtmlFromWrongAnswer + ).filterKeys { it != null } + .filterValues { it != null } + .mapKeys { it.key as String } + .mapValues { it.value as String } + + DragDropInteractionContentViewModel( + contentIdHtmlMap = updatedContentIdMap.ifEmpty { + contentIdHtmlMap + }, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = contentIdFromWrongAnswer ?: subtitledHtml.contentId + } + ) + }.build(), + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() + } else { + _originalChoiceItems.toMutableList() } - - Log.d("useranswerstring", "processEphemeralStateResult: choice strings - $choiceStrings") - Log.d("useranswerstring", "processEphemeralStateResult: choice items - $_choiceItems") - Log.d("useranswerstring", "processEphemeralStateResult: user answer list - ${userAnswerState.listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount}") - Log.d("useranswerstring", "processEphemeralStateResult: user answer list count - ${wrongAnswerList.lastOrNull()?.userAnswer?.listOfHtmlAnswers?.setOfHtmlStringsCount}") - - choiceStrings.mapIndexed { index, subtitledHtml -> - Log.d("useranswerstring", "processEphemeralStateResult: chs index - $index") - Log.d("useranswerstring", "processEphemeralStateResult: chs sbh - $subtitledHtml") - val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() - ?.userAnswer - ?.answer - ?.listOfSetsOfTranslatableHtmlContentIds - ?.contentIdListsList - ?.getOrNull(index) - ?.contentIdsList - ?.firstOrNull() - ?.contentId - - val contentHtmlFromWrongAnswer = wrongAnswerList?.lastOrNull() - ?.userAnswer - ?.listOfHtmlAnswers - ?.setOfHtmlStringsList - ?.get(index) - ?.htmlList - ?.firstOrNull() - - val updatedContentIdMap = mapOf( - contentIdFromWrongAnswer to contentHtmlFromWrongAnswer - ).filterKeys { it != null } - .filterValues { it != null } - .mapKeys { it.key as String } - .mapValues { it.value as String } - - DragDropInteractionContentViewModel( - contentIdHtmlMap = updatedContentIdMap.ifEmpty { - contentIdHtmlMap - }, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = contentIdFromWrongAnswer ?: subtitledHtml.contentId - } - ) - }.build(), - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() } } } From 58ef353b7acd402f6b556695fa0106857ee47395 Mon Sep 17 00:00:00 2001 From: Rd Date: Sun, 29 Sep 2024 15:27:26 +0530 Subject: [PATCH 09/21] Implementation of a retaining the reordered state with linking / grouping Needs a refactoring to combine them as one conditional return list and also to check the updation of the list on each drag end (doesn't occur with unlinkable elements). --- .../DragAndDropSortInteractionViewModel.kt | 115 +++++++++++------- 1 file changed, 69 insertions(+), 46 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index b5bbd30b7e7..25bfcd511ad 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -359,53 +359,76 @@ class DragAndDropSortInteractionViewModel private constructor( is AsyncResult.Success -> { val state = result.value val wrongAnswerList = state.pendingState.wrongAnswerList - val wrongAnswerListCount = wrongAnswerList.lastOrNull() - ?.userAnswer?.listOfHtmlAnswers?.setOfHtmlStringsCount - - if (choiceStrings.size == wrongAnswerListCount) { - choiceStrings.mapIndexed { index, subtitledHtml -> - val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() - ?.userAnswer - ?.answer - ?.listOfSetsOfTranslatableHtmlContentIds - ?.contentIdListsList - ?.getOrNull(index) - ?.contentIdsList - ?.firstOrNull() - ?.contentId - - val contentHtmlFromWrongAnswer = wrongAnswerList?.lastOrNull() - ?.userAnswer - ?.listOfHtmlAnswers - ?.setOfHtmlStringsList - ?.get(index) - ?.htmlList - ?.firstOrNull() - - val updatedContentIdMap = mapOf( - contentIdFromWrongAnswer to contentHtmlFromWrongAnswer - ).filterKeys { it != null } - .filterValues { it != null } - .mapKeys { it.key as String } - .mapValues { it.value as String } - - DragDropInteractionContentViewModel( - contentIdHtmlMap = updatedContentIdMap.ifEmpty { - contentIdHtmlMap - }, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = contentIdFromWrongAnswer ?: subtitledHtml.contentId + + if (wrongAnswerList.isNotEmpty()) { + val wrongAnswerListCount = wrongAnswerList.lastOrNull() + ?.userAnswer?.listOfHtmlAnswers?.setOfHtmlStringsCount + + if (choiceStrings.size == wrongAnswerListCount) { + choiceStrings.mapIndexed { index, subtitledHtml -> + val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() + ?.userAnswer + ?.answer + ?.listOfSetsOfTranslatableHtmlContentIds + ?.contentIdListsList + ?.getOrNull(index) + ?.contentIdsList + ?.firstOrNull() + ?.contentId + + val contentHtmlFromWrongAnswer = wrongAnswerList?.lastOrNull() + ?.userAnswer + ?.listOfHtmlAnswers + ?.setOfHtmlStringsList + ?.get(index) + ?.htmlList + ?.firstOrNull() + + val updatedContentIdMap = mapOf( + contentIdFromWrongAnswer to contentHtmlFromWrongAnswer + ).filterKeys { it != null } + .filterValues { it != null } + .mapKeys { it.key as String } + .mapValues { it.value as String } + + DragDropInteractionContentViewModel( + contentIdHtmlMap = updatedContentIdMap.ifEmpty { + contentIdHtmlMap + }, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = contentIdFromWrongAnswer ?: subtitledHtml.contentId + } + ) + }.build(), + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() + } else { + wrongAnswerList.last().userAnswer.answer.listOfSetsOfTranslatableHtmlContentIds.contentIdListsList.mapIndexed { index, setOfTranslatableHtmlContentIds -> + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + for (s in setOfTranslatableHtmlContentIds.contentIdsList) { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = s.contentId + } + ) } - ) - }.build(), - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() + }.build(), + itemIndex = index, + listSize = setOfTranslatableHtmlContentIds.contentIdsList.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() + + } } else { _originalChoiceItems.toMutableList() } From f7614457f4406557bc602ca43d3ace5b41f49175 Mon Sep 17 00:00:00 2001 From: Rd Date: Sun, 29 Sep 2024 23:02:06 +0530 Subject: [PATCH 10/21] Refactored the result retrieval with wrongAnswerList and added Robolectric test cases for merge and unlink drag drop elements --- .../DragAndDropSortInteractionViewModel.kt | 140 +++++++----------- .../app/player/state/StateFragmentTest.kt | 62 +++++++- 2 files changed, 112 insertions(+), 90 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 25bfcd511ad..4273620c44e 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -98,7 +98,6 @@ class DragAndDropSortInteractionViewModel private constructor( init { _choiceItems = computeSelectedChoiceItems( contentIdHtmlMap, - choiceSubtitledHtmls, this, resourceHandler ) @@ -342,7 +341,6 @@ class DragAndDropSortInteractionViewModel private constructor( private fun computeSelectedChoiceItems( contentIdHtmlMap: Map, - choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, resourceHandler: AppLanguageResourceHandler ): MutableList { @@ -350,94 +348,13 @@ class DragAndDropSortInteractionViewModel private constructor( explorationProgressController.getCurrentState().toLiveData() } - fun processEphemeralStateResult( - result: AsyncResult - ): MutableList { - return when (result) { - is AsyncResult.Failure -> mutableListOf() - is AsyncResult.Pending -> mutableListOf() - is AsyncResult.Success -> { - val state = result.value - val wrongAnswerList = state.pendingState.wrongAnswerList - - if (wrongAnswerList.isNotEmpty()) { - val wrongAnswerListCount = wrongAnswerList.lastOrNull() - ?.userAnswer?.listOfHtmlAnswers?.setOfHtmlStringsCount - - if (choiceStrings.size == wrongAnswerListCount) { - choiceStrings.mapIndexed { index, subtitledHtml -> - val contentIdFromWrongAnswer = wrongAnswerList?.lastOrNull() - ?.userAnswer - ?.answer - ?.listOfSetsOfTranslatableHtmlContentIds - ?.contentIdListsList - ?.getOrNull(index) - ?.contentIdsList - ?.firstOrNull() - ?.contentId - - val contentHtmlFromWrongAnswer = wrongAnswerList?.lastOrNull() - ?.userAnswer - ?.listOfHtmlAnswers - ?.setOfHtmlStringsList - ?.get(index) - ?.htmlList - ?.firstOrNull() - - val updatedContentIdMap = mapOf( - contentIdFromWrongAnswer to contentHtmlFromWrongAnswer - ).filterKeys { it != null } - .filterValues { it != null } - .mapKeys { it.key as String } - .mapValues { it.value as String } - - DragDropInteractionContentViewModel( - contentIdHtmlMap = updatedContentIdMap.ifEmpty { - contentIdHtmlMap - }, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = contentIdFromWrongAnswer ?: subtitledHtml.contentId - } - ) - }.build(), - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() - } else { - wrongAnswerList.last().userAnswer.answer.listOfSetsOfTranslatableHtmlContentIds.contentIdListsList.mapIndexed { index, setOfTranslatableHtmlContentIds -> - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - for (s in setOfTranslatableHtmlContentIds.contentIdsList) { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = s.contentId - } - ) - } - }.build(), - itemIndex = index, - listSize = setOfTranslatableHtmlContentIds.contentIdsList.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() - - } - } else { - _originalChoiceItems.toMutableList() - } - } - } - } - ephemeralStateLiveData.observe(fragment) { result -> - _choiceItems = processEphemeralStateResult(result) + _choiceItems = processEphemeralStateResult( + result, + contentIdHtmlMap, + dragAndDropSortInteractionViewModel, + resourceHandler + ) _choiceItemsLiveData.value = _choiceItems _originalChoiceItems = _choiceItems.toMutableList() } @@ -445,4 +362,49 @@ class DragAndDropSortInteractionViewModel private constructor( return _choiceItems.takeIf { it.isNotEmpty() } ?: _originalChoiceItems.toMutableList() } + + private fun processEphemeralStateResult( + result: AsyncResult, + contentIdHtmlMap: Map, + dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, + resourceHandler: AppLanguageResourceHandler + ): MutableList { + return when (result) { + is AsyncResult.Failure -> mutableListOf() + is AsyncResult.Pending -> mutableListOf() + is AsyncResult.Success -> { + val state = result.value + val wrongAnswerList = state.pendingState.wrongAnswerList + + if (wrongAnswerList.isNotEmpty()) { + val latestWrongAnswerContentIdList = wrongAnswerList.last() + .userAnswer + .answer + .listOfSetsOfTranslatableHtmlContentIds + .contentIdListsList + + latestWrongAnswerContentIdList.mapIndexed { index, setOfTranslatableHtmlContentIds -> + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + for (contentIds in setOfTranslatableHtmlContentIds.contentIdsList) { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = contentIds.contentId + } + ) + } + }.build(), + itemIndex = index, + listSize = latestWrongAnswerContentIdList.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() + } else { + _originalChoiceItems.toMutableList() + } + } + } + } } diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt index 6bb74d198a5..824535db5df 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt @@ -1139,7 +1139,7 @@ class StateFragmentTest { @Test @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. - fun testStateFragment_loadDragDropExp_mergeItems_dargAndDrop_retainStateOnConfigurationChange() { + fun testStateFragment_loadDragDropExp_mergeItems_dragAndDrop_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { startPlayingExploration() @@ -1327,6 +1327,66 @@ class StateFragmentTest { } } + @Test + fun testStateFragment_loadDragDropExp_mergeFirstTwoItems_wrongAnswer_retainsLatestState() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { + startPlayingExploration() + + mergeDragAndDropItems(position = 0) + clickSubmitAnswerButton() + + scrollToViewType(DRAG_DROP_SORT_INTERACTION) + onView(withId(R.id.drag_drop_interaction_recycler_view)).check(matches(hasChildCount(3))) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 0, + targetViewId = R.id.drag_drop_item_recyclerview + ) + ).check(matches(hasChildCount(2))) + } + } + + @Test + fun testStateFragment_loadDragDropExp_mergeUnlinkFirstTwoItems_wrongAnswer_retainsLatestState() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { + startPlayingExploration() + + mergeDragAndDropItems(position = 0) + unlinkDragAndDropItems(position = 0) + clickSubmitAnswerButton() + + scrollToViewType(DRAG_DROP_SORT_INTERACTION) + onView(withId(R.id.drag_drop_interaction_recycler_view)).check(matches(hasChildCount(4))) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 0, + targetViewId = R.id.drag_drop_item_recyclerview + ) + ).check(matches(hasChildCount(1))) + } + } + + @Test + fun testStateFragment_loadDragDropExp_mergeItems_unArrangedRetainState_causeSubmitTimeError() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { + startPlayingExploration() + + mergeDragAndDropItems(position = 0) + + clickSubmitAnswerButton() + clickSubmitAnswerButton() + + onView(withId(R.id.drag_drop_interaction_error)).check( + matches(withText(R.string.drag_and_drop_interaction_empty_input)) + ) + } + } + @Test @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. fun testStateFragment_loadDragDropExp_mergeFirstTwoItems_dragItem_worksCorrectly() { From aa4f2de2ec34088a0be884cfaf7ff29972361c02 Mon Sep 17 00:00:00 2001 From: Rd Date: Sun, 29 Sep 2024 23:04:46 +0530 Subject: [PATCH 11/21] Ktlint indentation fix --- .../DragAndDropSortInteractionViewModel.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 4273620c44e..95509a76cb7 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -377,11 +377,11 @@ class DragAndDropSortInteractionViewModel private constructor( val wrongAnswerList = state.pendingState.wrongAnswerList if (wrongAnswerList.isNotEmpty()) { - val latestWrongAnswerContentIdList = wrongAnswerList.last() - .userAnswer - .answer - .listOfSetsOfTranslatableHtmlContentIds - .contentIdListsList + val latestWrongAnswerContentIdList = wrongAnswerList.last() + .userAnswer + .answer + .listOfSetsOfTranslatableHtmlContentIds + .contentIdListsList latestWrongAnswerContentIdList.mapIndexed { index, setOfTranslatableHtmlContentIds -> DragDropInteractionContentViewModel( From 7d1ab3a63915b3f47e7d13799cd4d402b6350f0d Mon Sep 17 00:00:00 2001 From: Rd Date: Mon, 30 Sep 2024 01:21:54 +0530 Subject: [PATCH 12/21] Added test case to check the retained wrong Answer list text when merged / linked --- .../app/player/state/StateFragmentTest.kt | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt index 824535db5df..85954cf693e 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt @@ -1328,7 +1328,7 @@ class StateFragmentTest { } @Test - fun testStateFragment_loadDragDropExp_mergeFirstTwoItems_wrongAnswer_retainsLatestState() { + fun testStateFragment_loadDragDropExp_mergeFirstTwoItems_wrongAnswer_retainsLatestStateCount() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { startPlayingExploration() @@ -1348,6 +1348,33 @@ class StateFragmentTest { } } + @Test + fun testStateFragment_loadDragDropExp_mergeFirstTwoItems_wrongAnswer_retainsLatestStateText() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { + startPlayingExploration() + + mergeDragAndDropItems(position = 0) + clickSubmitAnswerButton() + + scrollToViewType(DRAG_DROP_SORT_INTERACTION) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_item_recyclerview, + position = 0, + targetViewId = R.id.drag_drop_content_text_view + ) + ).check(matches(withText("a camera at the store"))) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_item_recyclerview, + position = 1, + targetViewId = R.id.drag_drop_content_text_view + ) + ).check(matches(withText("I bought"))) + } + } + @Test fun testStateFragment_loadDragDropExp_mergeUnlinkFirstTwoItems_wrongAnswer_retainsLatestState() { setUpTestWithLanguageSwitchingFeatureOff() From 41f1195c4b0b6e02924fcf4e90a9d56d0c2d933c Mon Sep 17 00:00:00 2001 From: Rd Date: Tue, 1 Oct 2024 02:24:26 +0530 Subject: [PATCH 13/21] Added/Verified Espresso Tests - bumped Mockitor to 3.11.1 --- app/build.gradle | 6 +- .../app/player/state/StateFragmentTest.kt | 106 +++++++++++++++++- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7c94678f75c..c38647da28b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,7 +198,7 @@ dependencies { "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4', 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4', - 'org.mockito:mockito-core:2.7.22', + 'org.mockito:mockito-core:3.11.1', 'com.github.oppia:android-spotlight:cc23499d37dc8533a2876e45b5063e981a4583f4' ) compileOnly( @@ -222,7 +222,7 @@ dependencies { 'org.robolectric:robolectric:4.5', 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4', "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version", - 'org.mockito:mockito-core:2.7.22', + 'org.mockito:mockito-core:3.11.1', project(":testing"), ) androidTestImplementation( @@ -238,7 +238,7 @@ dependencies { 'androidx.work:work-testing:2.4.0', 'com.google.truth.extensions:truth-liteproto-extension:1.1.3', 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4', - 'org.mockito:mockito-android:2.7.22', + 'org.mockito:mockito-android:3.11.1', 'org.robolectric:annotations:4.5', ) // Adding the testing module directly causes duplicates of the below groups so we need to diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt index 85954cf693e..4f7ac99fc71 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt @@ -1327,6 +1327,80 @@ class StateFragmentTest { } } + @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + fun testStateFragment_loadDragDropExp_wrongAnswer_retainsLatestState() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + playThroughPrototypeState6() + playThroughPrototypeState7() + playThroughPrototypeState8() + + // Drag and drop interaction without grouping. + // Ninth state: Drag Drop Sort. Wrong answer: Move 1st item to 2nd position. + dragAndDropItem(fromPosition = 0, toPosition = 1) + clickSubmitAnswerButton() + + scrollToViewType(DRAG_DROP_SORT_INTERACTION) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 0, + targetViewId = R.id.drag_drop_content_text_view + ) + ).check(matches(withText("3/5"))) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 1, + targetViewId = R.id.drag_drop_content_text_view + ) + ).check(matches(withText("0.35"))) + } + } + + @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + fun testStateFragment_loadDragDropExp_wrongAnswer_unArrangedRetainState_causeSubmitTimeError() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + playThroughPrototypeState6() + playThroughPrototypeState7() + playThroughPrototypeState8() + + // Drag and drop interaction without grouping. + // Ninth state: Drag Drop Sort. Wrong answer: Move 1st item to 2nd position. + dragAndDropItem(fromPosition = 0, toPosition = 1) + clickSubmitAnswerButton() + + scrollToViewType(DRAG_DROP_SORT_INTERACTION) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 0, + targetViewId = R.id.drag_drop_content_text_view + ) + ).check(matches(withText("3/5"))) + clickSubmitAnswerButton() + + onView(withId(R.id.drag_drop_interaction_error)).check( + matches(withText(R.string.drag_and_drop_interaction_empty_input)) + ) + } + } + @Test fun testStateFragment_loadDragDropExp_mergeFirstTwoItems_wrongAnswer_retainsLatestStateCount() { setUpTestWithLanguageSwitchingFeatureOff() @@ -1398,14 +1472,44 @@ class StateFragmentTest { } @Test - fun testStateFragment_loadDragDropExp_mergeItems_unArrangedRetainState_causeSubmitTimeError() { + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + fun testStateFragment_loadDragDropExp_mergeItems_dragItem_wrongAnswer_retainsLatestState() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { startPlayingExploration() mergeDragAndDropItems(position = 0) + dragAndDropItem(fromPosition = 0, toPosition = 2) + clickSubmitAnswerButton() + scrollToViewType(DRAG_DROP_SORT_INTERACTION) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 2, + targetViewId = R.id.drag_drop_content_text_view + ) + ).check(matches(withText("a camera at the store"))) + } + } + + @Test + fun testStateFragment_loadDragDropExp_mergeItems_unArrangedRetainState_causeSubmitTimeError() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { + startPlayingExploration() + + mergeDragAndDropItems(position = 0) clickSubmitAnswerButton() + + scrollToViewType(DRAG_DROP_SORT_INTERACTION) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_item_recyclerview, + position = 0, + targetViewId = R.id.drag_drop_content_text_view + ) + ).check(matches(withText("a camera at the store"))) clickSubmitAnswerButton() onView(withId(R.id.drag_drop_interaction_error)).check( From 2989b1aa2885b2a949977a05b57944446e943bd7 Mon Sep 17 00:00:00 2001 From: Rd Date: Tue, 1 Oct 2024 21:56:42 +0530 Subject: [PATCH 14/21] Revert mockito version introduced to workaround locally --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c38647da28b..7c94678f75c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -198,7 +198,7 @@ dependencies { "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4', 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4', - 'org.mockito:mockito-core:3.11.1', + 'org.mockito:mockito-core:2.7.22', 'com.github.oppia:android-spotlight:cc23499d37dc8533a2876e45b5063e981a4583f4' ) compileOnly( @@ -222,7 +222,7 @@ dependencies { 'org.robolectric:robolectric:4.5', 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4', "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version", - 'org.mockito:mockito-core:3.11.1', + 'org.mockito:mockito-core:2.7.22', project(":testing"), ) androidTestImplementation( @@ -238,7 +238,7 @@ dependencies { 'androidx.work:work-testing:2.4.0', 'com.google.truth.extensions:truth-liteproto-extension:1.1.3', 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4', - 'org.mockito:mockito-android:3.11.1', + 'org.mockito:mockito-android:2.7.22', 'org.robolectric:annotations:4.5', ) // Adding the testing module directly causes duplicates of the below groups so we need to From 83349ef50a610ceffe0647ca7ffab37ad9360ddc Mon Sep 17 00:00:00 2001 From: Rd Date: Wed, 2 Oct 2024 14:05:04 +0530 Subject: [PATCH 15/21] Using the original choice items as a fall back value if the async result fails to retrieve data, making sure the original data is displayed instead of a empty list --- .../itemviewmodel/DragAndDropSortInteractionViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 95509a76cb7..3b7e6527449 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -370,8 +370,8 @@ class DragAndDropSortInteractionViewModel private constructor( resourceHandler: AppLanguageResourceHandler ): MutableList { return when (result) { - is AsyncResult.Failure -> mutableListOf() - is AsyncResult.Pending -> mutableListOf() + is AsyncResult.Failure -> _originalChoiceItems.toMutableList() + is AsyncResult.Pending -> _originalChoiceItems.toMutableList() is AsyncResult.Success -> { val state = result.value val wrongAnswerList = state.pendingState.wrongAnswerList From 6785bf5e2351aaba82495aca746342057a746a48 Mon Sep 17 00:00:00 2001 From: Rd Date: Wed, 20 Nov 2024 21:53:31 +0530 Subject: [PATCH 16/21] Using Transformations map to transform the live data Though this works with live data for _choiceItems and rendering them with the latest wrong answer list, it fails to update the original choice items list and also throws an inital null value (and had a hot fix to it). This was not unit tested. May be trying out switch map could be an option with the asyncrhonous data. --- .../state/DragDropSortInteractionView.kt | 7 +- .../DragAndDropSortInteractionViewModel.kt | 98 +++++++++---------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt index 706c3f57013..d8323b53491 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt @@ -2,6 +2,7 @@ package org.oppia.android.app.player.state import android.content.Context import android.util.AttributeSet +import android.util.Log import android.view.LayoutInflater import androidx.core.view.isVisible import androidx.fragment.app.Fragment @@ -75,7 +76,11 @@ class DragDropSortInteractionView @JvmOverloads constructor( * Note that this needs to be used instead of the generic RecyclerView 'data' binding adapter * since this one takes into account initialization order with other binding properties. */ - fun setDraggableData(dataList: List) { + fun setDraggableData(dataList: List?) { + if (dataList == null) { + Log.e("failingtocallobserver", "Received null dataList") + return + } this.dataList = dataList maybeInitializeAdapter() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 3b7e6527449..4e718b25d20 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -3,13 +3,11 @@ package org.oppia.android.app.player.state.itemviewmodel import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField -import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Transformations import androidx.recyclerview.widget.RecyclerView import org.oppia.android.R import org.oppia.android.app.model.AnswerErrorCategory -import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -28,11 +26,12 @@ import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.recyclerview.OnDragEndedListener import org.oppia.android.app.recyclerview.OnItemDragListener import org.oppia.android.app.translation.AppLanguageResourceHandler -import org.oppia.android.domain.exploration.ExplorationProgressController import org.oppia.android.domain.translation.TranslationController +import javax.inject.Inject +import org.oppia.android.app.model.EphemeralState +import org.oppia.android.domain.exploration.ExplorationProgressController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData -import javax.inject.Inject /** Represents the type of errors that can be thrown by drag and drop sort interaction. */ enum class DragAndDropSortInteractionError(@StringRes private var error: Int?) { @@ -57,13 +56,11 @@ class DragAndDropSortInteractionViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, userAnswerState: UserAnswerState, - private val fragment: Fragment, - private val explorationProgressController: ExplorationProgressController + explorationProgressController: ExplorationProgressController ) : StateItemViewModel(ViewType.DRAG_DROP_SORT_INTERACTION), InteractionAnswerHandler, OnItemDragListener, OnDragEndedListener { - private val allowMultipleItemsInSamePosition: Boolean by lazy { interaction.customizationArgsMap["allowMultipleItemsInSamePosition"]?.boolValue ?: false } @@ -87,23 +84,28 @@ class DragAndDropSortInteractionViewModel private constructor( private var _originalChoiceItems: MutableList = computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) - private var _choiceItems: MutableList = mutableListOf() - private val _choiceItemsLiveData = MutableLiveData>() - val choiceItems: LiveData> = _choiceItemsLiveData + private var _choiceItems = computeSelectedChoiceItems( + contentIdHtmlMap, + choiceSubtitledHtmls, + this, + resourceHandler, + userAnswerState + ) + + private val choiceItemsLiveData: LiveData> by lazy { + Transformations.map( + explorationProgressController.getCurrentState().toLiveData(), + ::processChoiceItems + ) + } + + val choiceItems = choiceItemsLiveData private var pendingAnswerError: String? = null private val isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") init { - _choiceItems = computeSelectedChoiceItems( - contentIdHtmlMap, - this, - resourceHandler - ) - - _choiceItemsLiveData.value = _choiceItems - val callback: Observable.OnPropertyChangedCallback = object : Observable.OnPropertyChangedCallback() { override fun onPropertyChanged(sender: Observable, propertyId: Int) { @@ -266,7 +268,6 @@ class DragAndDropSortInteractionViewModel private constructor( class FactoryImpl @Inject constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, - private val fragment: Fragment, private val explorationProgressController: ExplorationProgressController ) : InteractionItemFactory { override fun create( @@ -291,7 +292,6 @@ class DragAndDropSortInteractionViewModel private constructor( resourceHandler, translationController, userAnswerState, - fragment, explorationProgressController ) } @@ -341,39 +341,34 @@ class DragAndDropSortInteractionViewModel private constructor( private fun computeSelectedChoiceItems( contentIdHtmlMap: Map, + choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler + resourceHandler: AppLanguageResourceHandler, + userAnswerState: UserAnswerState ): MutableList { - val ephemeralStateLiveData: LiveData> by lazy { - explorationProgressController.getCurrentState().toLiveData() - } - - ephemeralStateLiveData.observe(fragment) { result -> - _choiceItems = processEphemeralStateResult( - result, - contentIdHtmlMap, - dragAndDropSortInteractionViewModel, - resourceHandler - ) - _choiceItemsLiveData.value = _choiceItems - _originalChoiceItems = _choiceItems.toMutableList() + return if (userAnswerState.listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount == 0) { + _originalChoiceItems.toMutableList() + } else { + userAnswerState.listOfSetsOfTranslatableHtmlContentIds.contentIdListsList + .mapIndexed { index, contentId -> + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = contentId, + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() } - - return _choiceItems.takeIf { it.isNotEmpty() } - ?: _originalChoiceItems.toMutableList() } - private fun processEphemeralStateResult( - result: AsyncResult, - contentIdHtmlMap: Map, - dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler - ): MutableList { - return when (result) { - is AsyncResult.Failure -> _originalChoiceItems.toMutableList() - is AsyncResult.Pending -> _originalChoiceItems.toMutableList() + private fun processChoiceItems(stateResult: AsyncResult): List { + return when(stateResult) { + is AsyncResult.Pending -> _originalChoiceItems + is AsyncResult.Failure -> _originalChoiceItems is AsyncResult.Success -> { - val state = result.value + val state = stateResult.value val wrongAnswerList = state.pendingState.wrongAnswerList if (wrongAnswerList.isNotEmpty()) { @@ -383,7 +378,7 @@ class DragAndDropSortInteractionViewModel private constructor( .listOfSetsOfTranslatableHtmlContentIds .contentIdListsList - latestWrongAnswerContentIdList.mapIndexed { index, setOfTranslatableHtmlContentIds -> + _choiceItems = latestWrongAnswerContentIdList.mapIndexed { index, setOfTranslatableHtmlContentIds -> DragDropInteractionContentViewModel( contentIdHtmlMap = contentIdHtmlMap, htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { @@ -397,12 +392,13 @@ class DragAndDropSortInteractionViewModel private constructor( }.build(), itemIndex = index, listSize = latestWrongAnswerContentIdList.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + dragAndDropSortInteractionViewModel = this, resourceHandler = resourceHandler ) }.toMutableList() + _choiceItems } else { - _originalChoiceItems.toMutableList() + _originalChoiceItems } } } From ee958848406531b27701a998b0a6563d10a7e49c Mon Sep 17 00:00:00 2001 From: Rd Date: Thu, 21 Nov 2024 14:37:32 +0530 Subject: [PATCH 17/21] Utilized Transformations switch map to proide the live data To remove debugging logs and test other functionalities --- .../DragAndDropSortInteractionViewModel.kt | 191 +++++++++++------- 1 file changed, 117 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 4e718b25d20..79c215f14c9 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -1,13 +1,17 @@ package org.oppia.android.app.player.state.itemviewmodel +import android.util.Log import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField +import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.recyclerview.widget.RecyclerView import org.oppia.android.R import org.oppia.android.app.model.AnswerErrorCategory +import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -26,12 +30,11 @@ import org.oppia.android.app.recyclerview.BindableAdapter import org.oppia.android.app.recyclerview.OnDragEndedListener import org.oppia.android.app.recyclerview.OnItemDragListener import org.oppia.android.app.translation.AppLanguageResourceHandler -import org.oppia.android.domain.translation.TranslationController -import javax.inject.Inject -import org.oppia.android.app.model.EphemeralState import org.oppia.android.domain.exploration.ExplorationProgressController +import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.data.AsyncResult import org.oppia.android.util.data.DataProviders.Companion.toLiveData +import javax.inject.Inject /** Represents the type of errors that can be thrown by drag and drop sort interaction. */ enum class DragAndDropSortInteractionError(@StringRes private var error: Int?) { @@ -56,11 +59,12 @@ class DragAndDropSortInteractionViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, userAnswerState: UserAnswerState, - explorationProgressController: ExplorationProgressController + private val explorationProgressController: ExplorationProgressController ) : StateItemViewModel(ViewType.DRAG_DROP_SORT_INTERACTION), InteractionAnswerHandler, OnItemDragListener, OnDragEndedListener { + private val allowMultipleItemsInSamePosition: Boolean by lazy { interaction.customizationArgsMap["allowMultipleItemsInSamePosition"]?.boolValue ?: false } @@ -84,28 +88,31 @@ class DragAndDropSortInteractionViewModel private constructor( private var _originalChoiceItems: MutableList = computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) - private var _choiceItems = computeSelectedChoiceItems( - contentIdHtmlMap, - choiceSubtitledHtmls, - this, - resourceHandler, - userAnswerState - ) - - private val choiceItemsLiveData: LiveData> by lazy { - Transformations.map( - explorationProgressController.getCurrentState().toLiveData(), - ::processChoiceItems - ) - } - - val choiceItems = choiceItemsLiveData + private var _choiceItems: MutableList = mutableListOf() + private var _choiceItemsLiveData = MutableLiveData>() + var choiceItems: LiveData> = _choiceItemsLiveData private var pendingAnswerError: String? = null private val isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") init { + Log.d("checkingchoiceitems", "init: _originalChoiceItems - $_originalChoiceItems") + Log.d("checkingchoiceitems", "init: _choiceItems - $_choiceItems") + Log.d("checkingchoiceitems", "init: _choiceItemsLiveData value - ${_choiceItemsLiveData.value}") + Log.d("checkingchoiceitems", "init: choiceItems value - ${choiceItems.value}") + + _choiceItems = computeSelectedChoiceItems( + contentIdHtmlMap, + this, + resourceHandler + ) + + _choiceItemsLiveData.value = _choiceItems + + Log.d("checkingchoiceitems", "init: after setting _choiceItems - $_choiceItems") + Log.d("checkingchoiceitems", "init: after setting _choiceItemsLiveData value - ${_choiceItemsLiveData.value}") + val callback: Observable.OnPropertyChangedCallback = object : Observable.OnPropertyChangedCallback() { override fun onPropertyChanged(sender: Observable, propertyId: Int) { @@ -341,66 +348,102 @@ class DragAndDropSortInteractionViewModel private constructor( private fun computeSelectedChoiceItems( contentIdHtmlMap: Map, - choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler, - userAnswerState: UserAnswerState + resourceHandler: AppLanguageResourceHandler ): MutableList { - return if (userAnswerState.listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount == 0) { - _originalChoiceItems.toMutableList() - } else { - userAnswerState.listOfSetsOfTranslatableHtmlContentIds.contentIdListsList - .mapIndexed { index, contentId -> - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = contentId, - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() + val ephemeralStateLiveData: LiveData> by lazy { + explorationProgressController.getCurrentState().toLiveData() } - } - - private fun processChoiceItems(stateResult: AsyncResult): List { - return when(stateResult) { - is AsyncResult.Pending -> _originalChoiceItems - is AsyncResult.Failure -> _originalChoiceItems - is AsyncResult.Success -> { - val state = stateResult.value - val wrongAnswerList = state.pendingState.wrongAnswerList - - if (wrongAnswerList.isNotEmpty()) { - val latestWrongAnswerContentIdList = wrongAnswerList.last() - .userAnswer - .answer - .listOfSetsOfTranslatableHtmlContentIds - .contentIdListsList - - _choiceItems = latestWrongAnswerContentIdList.mapIndexed { index, setOfTranslatableHtmlContentIds -> - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - for (contentIds in setOfTranslatableHtmlContentIds.contentIdsList) { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = contentIds.contentId - } - ) - } - }.build(), - itemIndex = index, - listSize = latestWrongAnswerContentIdList.size, - dragAndDropSortInteractionViewModel = this, - resourceHandler = resourceHandler + Log.d("checkingchoiceitems", "computeSelectedChoices ephemeral state live data - $ephemeralStateLiveData") + + /*ephemeralStateLiveData.observe(fragment) { result -> + _choiceItems = processEphemeralStateResult( + result, + contentIdHtmlMap, + dragAndDropSortInteractionViewModel, + resourceHandler + ) + _choiceItemsLiveData.value = _choiceItems + _originalChoiceItems = _choiceItems.toMutableList() + }*/ + + Log.d("checkingchoiceitems", "computeSelectedChoices choiceItems before switch map- ${choiceItems.value}") + + choiceItems = Transformations.switchMap(ephemeralStateLiveData) { result -> + Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map") + MutableLiveData>().apply { + value = when (result) { + is AsyncResult.Failure, is AsyncResult.Pending -> _originalChoiceItems + is AsyncResult.Success -> { + Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map success case") + // try choice items here + _choiceItems = processEphemeralStateResult( + result.value, + contentIdHtmlMap, + dragAndDropSortInteractionViewModel, + resourceHandler ) - }.toMutableList() - _choiceItems - } else { - _originalChoiceItems + _choiceItemsLiveData.value = _choiceItems + _originalChoiceItems = _choiceItems.toMutableList() + _choiceItems + } } + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map") + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _choiceItems - $_choiceItems") + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _choiceItemsLiveData - ${_choiceItemsLiveData.value}") + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _originalChoiceItems - $_originalChoiceItems") } } + + Log.d("checkingchoiceitems", "computeSelectedChoices choiceItems after switch map- ${choiceItems.value}") + Log.d("checkingchoiceitems", "computeSelectedChoices _choiceItems after switch map- $_choiceItems") + Log.d("checkingchoiceitems", "computeSelectedChoices _originalChoiceItems after switch map - $_originalChoiceItems") + + /*if (choiceItems.value != null) { + _choiceItems = choiceItems.value as MutableList + }*/ + + Log.d("swichmapchoiceitems", "computeSelectedChoiceItems: _choiceItems $_choiceItems") + + /*choiceItems = _choiceItemsLiveData + _choiceItems = _choiceItemsLiveData.value?.toMutableList()!!*/ + + return _choiceItems.takeIf { it.isNotEmpty() } + ?: _originalChoiceItems.toMutableList() + } + + private fun processEphemeralStateResult( + state: EphemeralState, + contentIdHtmlMap: Map, + dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, + resourceHandler: AppLanguageResourceHandler + ): MutableList { + Log.d("checkingchoiceitems", "In process ephemeral state result") + val wrongAnswerList = state.pendingState.wrongAnswerList + return if (wrongAnswerList.isNotEmpty()) { + val latestWrongAnswerContentIdList = wrongAnswerList.last() + .userAnswer.answer.listOfSetsOfTranslatableHtmlContentIds.contentIdListsList + latestWrongAnswerContentIdList.mapIndexed { index, setOfTranslatableHtmlContentIds -> + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + for (contentIds in setOfTranslatableHtmlContentIds.contentIdsList) { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = contentIds.contentId + } + ) + } + }.build(), + itemIndex = index, + listSize = latestWrongAnswerContentIdList.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() + } else { + Log.d("checkingchoiceitems", "In else of process ephemeral state result") + _originalChoiceItems.toMutableList() + } } } From 2620f1a048ed4f9d68bfd007305509266edc45e1 Mon Sep 17 00:00:00 2001 From: Rd Date: Thu, 21 Nov 2024 22:19:37 +0530 Subject: [PATCH 18/21] Utilized MediatorLiveData to set a initial Pending value to avoid NPE --- .../state/DragDropSortInteractionView.kt | 6 +- .../DragAndDropSortInteractionViewModel.kt | 111 ++++++++++++++++-- 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt index d8323b53491..365ad640831 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt @@ -76,11 +76,7 @@ class DragDropSortInteractionView @JvmOverloads constructor( * Note that this needs to be used instead of the generic RecyclerView 'data' binding adapter * since this one takes into account initialization order with other binding properties. */ - fun setDraggableData(dataList: List?) { - if (dataList == null) { - Log.e("failingtocallobserver", "Received null dataList") - return - } + fun setDraggableData(dataList: List) { this.dataList = dataList maybeInitializeAdapter() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 79c215f14c9..fc941c2f4cf 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -6,6 +6,7 @@ import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.recyclerview.widget.RecyclerView @@ -89,7 +90,7 @@ class DragAndDropSortInteractionViewModel private constructor( computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) private var _choiceItems: MutableList = mutableListOf() - private var _choiceItemsLiveData = MutableLiveData>() + private var _choiceItemsLiveData = MutableLiveData>(_originalChoiceItems) var choiceItems: LiveData> = _choiceItemsLiveData private var pendingAnswerError: String? = null @@ -351,10 +352,14 @@ class DragAndDropSortInteractionViewModel private constructor( dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, resourceHandler: AppLanguageResourceHandler ): MutableList { - val ephemeralStateLiveData: LiveData> by lazy { + /*val ephemeralStateLiveData: LiveData> by lazy { explorationProgressController.getCurrentState().toLiveData() - } + }*/ + + val ephemeralStateLiveData: LiveData> = explorationProgressController.getCurrentState().toLiveData() + Log.d("checkingchoiceitems", "computeSelectedChoices ephemeral state live data - $ephemeralStateLiveData") + Log.d("checkingchoiceitems", "computeSelectedChoices ephemeral state live data value - ${ephemeralStateLiveData.value}") /*ephemeralStateLiveData.observe(fragment) { result -> _choiceItems = processEphemeralStateResult( @@ -369,12 +374,25 @@ class DragAndDropSortInteractionViewModel private constructor( Log.d("checkingchoiceitems", "computeSelectedChoices choiceItems before switch map- ${choiceItems.value}") - choiceItems = Transformations.switchMap(ephemeralStateLiveData) { result -> + + /*val filteredEphemeralStateLiveData = MediatorLiveData>().apply { + addSource(ephemeralStateLiveData) { result -> + if (result != null) { + value = result + } + } + } + + +// choiceItems = Transformations.switchMap(ephemeralStateLiveData) { result -> +// choiceItems = Transformations.switchMap(ephemeralStateLiveData) { result -> + choiceItems = Transformations.switchMap(filteredEphemeralStateLiveData) { result -> Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map") MutableLiveData>().apply { value = when (result) { is AsyncResult.Failure, is AsyncResult.Pending -> _originalChoiceItems is AsyncResult.Success -> { + Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map ephemeral live data - ${ephemeralStateLiveData.value}") Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map success case") // try choice items here _choiceItems = processEphemeralStateResult( @@ -384,17 +402,94 @@ class DragAndDropSortInteractionViewModel private constructor( resourceHandler ) _choiceItemsLiveData.value = _choiceItems + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins ephemeral _choiceItemsLiveData - ${_choiceItemsLiveData.value}") _originalChoiceItems = _choiceItems.toMutableList() _choiceItems } } - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map") - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _choiceItems - $_choiceItems") - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _choiceItemsLiveData - ${_choiceItemsLiveData.value}") - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _originalChoiceItems - $_originalChoiceItems") + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map") + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _choiceItems - $_choiceItems") + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _choiceItemsLiveData - ${_choiceItemsLiveData.value}") + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _originalChoiceItems - $_originalChoiceItems") + } + } as MutableLiveData>*/ + + + + + + + + + // Create a MediatorLiveData that only updates when the value is non-null + val filteredEphemeralStateLiveData = MediatorLiveData>().apply { + value = AsyncResult.Pending() + addSource(ephemeralStateLiveData) { result -> +// if (result != null) { + value = result +// } } } + // this works +// Now switchMap with the filtered LiveData +// choiceItems = Transformations.switchMap(filteredEphemeralStateLiveData) { result -> + /*choiceItems = Transformations.switchMap(filteredEphemeralStateLiveData) { result -> + Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map") + MutableLiveData>().apply { + value = when (result) { + is AsyncResult.Failure, is AsyncResult.Pending -> { + _originalChoiceItems + } + is AsyncResult.Success -> { + Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map success case") + _choiceItems = processEphemeralStateResult( + result.value, + contentIdHtmlMap, + dragAndDropSortInteractionViewModel, + resourceHandler + ) + _choiceItemsLiveData.value = _choiceItems + _originalChoiceItems = _choiceItems.toMutableList() + _choiceItems // Return the processed choice items + } + else -> _originalChoiceItems + } + Log.d("checkingchoiceitems", "computeSelectedChoices end of transformations switch map") + } + }*/ + + + + choiceItems = Transformations.map(filteredEphemeralStateLiveData) { result -> + Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map") + when (result) { + is AsyncResult.Failure, is AsyncResult.Pending -> { + Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map pending case") + _originalChoiceItems + } + is AsyncResult.Success -> { + Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map success case") + _choiceItems = processEphemeralStateResult( + result.value, + contentIdHtmlMap, + dragAndDropSortInteractionViewModel, + resourceHandler + ) + _choiceItemsLiveData.value = _choiceItems + _originalChoiceItems = _choiceItems.toMutableList() + _choiceItems + } + else -> _originalChoiceItems + } + } + + + + + + + Log.d("checkingchoiceitems", "computeSelectedChoices choiceItems after switch map- ${choiceItems.value}") Log.d("checkingchoiceitems", "computeSelectedChoices _choiceItems after switch map- $_choiceItems") Log.d("checkingchoiceitems", "computeSelectedChoices _originalChoiceItems after switch map - $_originalChoiceItems") From 1b97d3d4899ff761bda67caf8d09377f2db5f81d Mon Sep 17 00:00:00 2001 From: Rd Date: Fri, 22 Nov 2024 13:35:43 +0530 Subject: [PATCH 19/21] Cleanup of debugging logs and intermediate values Tests passed on Espresso --- .../DragAndDropSortInteractionViewModel.kt | 164 ++---------------- 1 file changed, 11 insertions(+), 153 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index fc941c2f4cf..77d6ee0c9a9 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -1,13 +1,10 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField -import androidx.fragment.app.Fragment import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations import androidx.recyclerview.widget.RecyclerView import org.oppia.android.R @@ -89,31 +86,19 @@ class DragAndDropSortInteractionViewModel private constructor( private var _originalChoiceItems: MutableList = computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) - private var _choiceItems: MutableList = mutableListOf() - private var _choiceItemsLiveData = MutableLiveData>(_originalChoiceItems) - var choiceItems: LiveData> = _choiceItemsLiveData - - private var pendingAnswerError: String? = null - private val isAnswerAvailable = ObservableField(false) - var errorMessage = ObservableField("") - - init { - Log.d("checkingchoiceitems", "init: _originalChoiceItems - $_originalChoiceItems") - Log.d("checkingchoiceitems", "init: _choiceItems - $_choiceItems") - Log.d("checkingchoiceitems", "init: _choiceItemsLiveData value - ${_choiceItemsLiveData.value}") - Log.d("checkingchoiceitems", "init: choiceItems value - ${choiceItems.value}") - - _choiceItems = computeSelectedChoiceItems( + lateinit var choiceItems: LiveData> + private var _choiceItems: MutableList = + computeSelectedChoiceItems( contentIdHtmlMap, this, resourceHandler ) - _choiceItemsLiveData.value = _choiceItems - - Log.d("checkingchoiceitems", "init: after setting _choiceItems - $_choiceItems") - Log.d("checkingchoiceitems", "init: after setting _choiceItemsLiveData value - ${_choiceItemsLiveData.value}") + private var pendingAnswerError: String? = null + private val isAnswerAvailable = ObservableField(false) + var errorMessage = ObservableField("") + init { val callback: Observable.OnPropertyChangedCallback = object : Observable.OnPropertyChangedCallback() { override fun onPropertyChanged(sender: Observable, propertyId: Int) { @@ -352,131 +337,25 @@ class DragAndDropSortInteractionViewModel private constructor( dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, resourceHandler: AppLanguageResourceHandler ): MutableList { - /*val ephemeralStateLiveData: LiveData> by lazy { - explorationProgressController.getCurrentState().toLiveData() - }*/ - - val ephemeralStateLiveData: LiveData> = explorationProgressController.getCurrentState().toLiveData() - - Log.d("checkingchoiceitems", "computeSelectedChoices ephemeral state live data - $ephemeralStateLiveData") - Log.d("checkingchoiceitems", "computeSelectedChoices ephemeral state live data value - ${ephemeralStateLiveData.value}") - - /*ephemeralStateLiveData.observe(fragment) { result -> - _choiceItems = processEphemeralStateResult( - result, - contentIdHtmlMap, - dragAndDropSortInteractionViewModel, - resourceHandler - ) - _choiceItemsLiveData.value = _choiceItems - _originalChoiceItems = _choiceItems.toMutableList() - }*/ - - Log.d("checkingchoiceitems", "computeSelectedChoices choiceItems before switch map- ${choiceItems.value}") - - - /*val filteredEphemeralStateLiveData = MediatorLiveData>().apply { - addSource(ephemeralStateLiveData) { result -> - if (result != null) { - value = result - } - } - } - - -// choiceItems = Transformations.switchMap(ephemeralStateLiveData) { result -> -// choiceItems = Transformations.switchMap(ephemeralStateLiveData) { result -> - choiceItems = Transformations.switchMap(filteredEphemeralStateLiveData) { result -> - Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map") - MutableLiveData>().apply { - value = when (result) { - is AsyncResult.Failure, is AsyncResult.Pending -> _originalChoiceItems - is AsyncResult.Success -> { - Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map ephemeral live data - ${ephemeralStateLiveData.value}") - Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map success case") - // try choice items here - _choiceItems = processEphemeralStateResult( - result.value, - contentIdHtmlMap, - dragAndDropSortInteractionViewModel, - resourceHandler - ) - _choiceItemsLiveData.value = _choiceItems - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins ephemeral _choiceItemsLiveData - ${_choiceItemsLiveData.value}") - _originalChoiceItems = _choiceItems.toMutableList() - _choiceItems - } - } - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map") - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _choiceItems - $_choiceItems") - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _choiceItemsLiveData - ${_choiceItemsLiveData.value}") - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformatins switch map _originalChoiceItems - $_originalChoiceItems") - } - } as MutableLiveData>*/ - - - - - - - - - // Create a MediatorLiveData that only updates when the value is non-null - val filteredEphemeralStateLiveData = MediatorLiveData>().apply { + val explorationEphemeralStateLiveData = MediatorLiveData>().apply { value = AsyncResult.Pending() - addSource(ephemeralStateLiveData) { result -> -// if (result != null) { + addSource(explorationProgressController.getCurrentState().toLiveData()) { result -> value = result -// } } } - // this works -// Now switchMap with the filtered LiveData -// choiceItems = Transformations.switchMap(filteredEphemeralStateLiveData) { result -> - /*choiceItems = Transformations.switchMap(filteredEphemeralStateLiveData) { result -> - Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map") - MutableLiveData>().apply { - value = when (result) { - is AsyncResult.Failure, is AsyncResult.Pending -> { - _originalChoiceItems - } - is AsyncResult.Success -> { - Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map success case") - _choiceItems = processEphemeralStateResult( - result.value, - contentIdHtmlMap, - dragAndDropSortInteractionViewModel, - resourceHandler - ) - _choiceItemsLiveData.value = _choiceItems - _originalChoiceItems = _choiceItems.toMutableList() - _choiceItems // Return the processed choice items - } - else -> _originalChoiceItems - } - Log.d("checkingchoiceitems", "computeSelectedChoices end of transformations switch map") - } - }*/ - - - - choiceItems = Transformations.map(filteredEphemeralStateLiveData) { result -> - Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map") + choiceItems = Transformations.map(explorationEphemeralStateLiveData) { result -> when (result) { is AsyncResult.Failure, is AsyncResult.Pending -> { - Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map pending case") _originalChoiceItems } is AsyncResult.Success -> { - Log.d("checkingchoiceitems", "computeSelectedChoices in transformations switch map success case") _choiceItems = processEphemeralStateResult( result.value, contentIdHtmlMap, dragAndDropSortInteractionViewModel, resourceHandler ) - _choiceItemsLiveData.value = _choiceItems _originalChoiceItems = _choiceItems.toMutableList() _choiceItems } @@ -484,26 +363,7 @@ class DragAndDropSortInteractionViewModel private constructor( } } - - - - - - - Log.d("checkingchoiceitems", "computeSelectedChoices choiceItems after switch map- ${choiceItems.value}") - Log.d("checkingchoiceitems", "computeSelectedChoices _choiceItems after switch map- $_choiceItems") - Log.d("checkingchoiceitems", "computeSelectedChoices _originalChoiceItems after switch map - $_originalChoiceItems") - - /*if (choiceItems.value != null) { - _choiceItems = choiceItems.value as MutableList - }*/ - - Log.d("swichmapchoiceitems", "computeSelectedChoiceItems: _choiceItems $_choiceItems") - - /*choiceItems = _choiceItemsLiveData - _choiceItems = _choiceItemsLiveData.value?.toMutableList()!!*/ - - return _choiceItems.takeIf { it.isNotEmpty() } + return _choiceItems.takeIf { !it.isNullOrEmpty() } ?: _originalChoiceItems.toMutableList() } @@ -513,7 +373,6 @@ class DragAndDropSortInteractionViewModel private constructor( dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, resourceHandler: AppLanguageResourceHandler ): MutableList { - Log.d("checkingchoiceitems", "In process ephemeral state result") val wrongAnswerList = state.pendingState.wrongAnswerList return if (wrongAnswerList.isNotEmpty()) { val latestWrongAnswerContentIdList = wrongAnswerList.last() @@ -537,7 +396,6 @@ class DragAndDropSortInteractionViewModel private constructor( ) }.toMutableList() } else { - Log.d("checkingchoiceitems", "In else of process ephemeral state result") _originalChoiceItems.toMutableList() } } From a82e5d4b2c879cde7a597c795a0844258dcea6e0 Mon Sep 17 00:00:00 2001 From: Rd Date: Fri, 22 Nov 2024 13:47:10 +0530 Subject: [PATCH 20/21] Fix ktlint indentation issues --- .../state/DragDropSortInteractionView.kt | 1 - .../DragAndDropSortInteractionViewModel.kt | 32 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt index 365ad640831..706c3f57013 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/DragDropSortInteractionView.kt @@ -2,7 +2,6 @@ package org.oppia.android.app.player.state import android.content.Context import android.util.AttributeSet -import android.util.Log import android.view.LayoutInflater import androidx.core.view.isVisible import androidx.fragment.app.Fragment diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 77d6ee0c9a9..747b58f7761 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -340,28 +340,28 @@ class DragAndDropSortInteractionViewModel private constructor( val explorationEphemeralStateLiveData = MediatorLiveData>().apply { value = AsyncResult.Pending() addSource(explorationProgressController.getCurrentState().toLiveData()) { result -> - value = result + value = result } } choiceItems = Transformations.map(explorationEphemeralStateLiveData) { result -> - when (result) { - is AsyncResult.Failure, is AsyncResult.Pending -> { - _originalChoiceItems - } - is AsyncResult.Success -> { - _choiceItems = processEphemeralStateResult( - result.value, - contentIdHtmlMap, - dragAndDropSortInteractionViewModel, - resourceHandler - ) - _originalChoiceItems = _choiceItems.toMutableList() - _choiceItems - } - else -> _originalChoiceItems + when (result) { + is AsyncResult.Failure, is AsyncResult.Pending -> { + _originalChoiceItems + } + is AsyncResult.Success -> { + _choiceItems = processEphemeralStateResult( + result.value, + contentIdHtmlMap, + dragAndDropSortInteractionViewModel, + resourceHandler + ) + _originalChoiceItems = _choiceItems.toMutableList() + _choiceItems } + else -> _originalChoiceItems } + } return _choiceItems.takeIf { !it.isNullOrEmpty() } ?: _originalChoiceItems.toMutableList() From 1863715e6f30a9db28cd89c23a42a141079da123 Mon Sep 17 00:00:00 2001 From: Rd Date: Fri, 22 Nov 2024 17:30:49 +0530 Subject: [PATCH 21/21] Fix spelling of variable - answerErrorCategory --- .../itemviewmodel/DragAndDropSortInteractionViewModel.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 747b58f7761..7abeb9fff1f 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -81,7 +81,7 @@ class DragAndDropSortInteractionViewModel private constructor( subtitledHtml.contentId to translatedHtml } - private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR + private var answerErrorCategory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR private var _originalChoiceItems: MutableList = computeOriginalChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) @@ -178,7 +178,7 @@ class DragAndDropSortInteractionViewModel private constructor( * updates the error string based on the specified error category. */ override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { - answerErrorCetegory = category + answerErrorCategory = category pendingAnswerError = when (category) { AnswerErrorCategory.REAL_TIME -> null AnswerErrorCategory.SUBMIT_TIME -> @@ -293,7 +293,7 @@ class DragAndDropSortInteractionViewModel private constructor( override fun getUserAnswerState(): UserAnswerState { if (_choiceItems == _originalChoiceItems) { return UserAnswerState.newBuilder().apply { - this.answerErrorCategory = answerErrorCetegory + this.answerErrorCategory = answerErrorCategory }.build() } return UserAnswerState.newBuilder().apply { @@ -302,7 +302,7 @@ class DragAndDropSortInteractionViewModel private constructor( ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply { addAllContentIdLists(htmlContentIds) }.build() - answerErrorCategory = answerErrorCetegory + answerErrorCategory = answerErrorCategory }.build() }