From 32ee035c0551570900d4facdbdc04f981b49f757 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Sat, 6 Jul 2024 11:54:40 +0530 Subject: [PATCH 01/18] selection interaction state retain --- .../android/app/player/state/StateFragment.kt | 21 ++++++++++++- .../player/state/StateFragmentPresenter.kt | 14 +++++++-- .../state/StatePlayerRecyclerViewAssembler.kt | 22 ++++++++++---- .../app/player/state/StateViewModel.kt | 8 +++++ .../InteractionAnswerHandler.kt | 6 ++++ .../ContinueInteractionViewModel.kt | 4 ++- .../DragAndDropSortInteractionViewModel.kt | 4 ++- .../FractionInteractionViewModel.kt | 4 ++- ...mageRegionSelectionInteractionViewModel.kt | 4 ++- .../MathExpressionInteractionsViewModel.kt | 4 ++- .../itemviewmodel/NumericInputViewModel.kt | 4 ++- ...atioExpressionInputInteractionViewModel.kt | 4 ++- .../SelectionInteractionViewModel.kt | 30 ++++++++++++++++--- .../state/itemviewmodel/StateItemViewModel.kt | 4 ++- .../state/itemviewmodel/TextInputViewModel.kt | 4 ++- model/src/main/proto/exploration.proto | 29 ++++++++++++++++++ 16 files changed, 144 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt index ca7c1d55565..fa2230df8e1 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt @@ -23,6 +23,7 @@ import org.oppia.android.app.player.state.listener.SubmitNavigationButtonListene import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** Fragment that represents the current state of an exploration. */ class StateFragment : @@ -42,6 +43,9 @@ class StateFragment : /** Arguments key for StateFragment. */ const val STATE_FRAGMENT_ARGUMENTS_KEY = "StateFragment.arguments" + /** Arguments key for StateFragment. */ + const val STATE_FRAGMENT_USER_ANSWER_STATE_KEY = "StateFragment.user_answer_state" + /** * Creates a new instance of a StateFragment. * @param internalProfileId used by StateFragment to mark progress. @@ -86,6 +90,12 @@ class StateFragment : ): View? { val args = arguments?.getProto(STATE_FRAGMENT_ARGUMENTS_KEY, StateFragmentArguments.getDefaultInstance()) + + val userAnswerState = savedInstanceState?.getProto( + STATE_FRAGMENT_USER_ANSWER_STATE_KEY, + UserAnswerState.getDefaultInstance() + ) ?: UserAnswerState.getDefaultInstance() + val internalProfileId = args?.internalProfileId ?: -1 val topicId = args?.topicId!! val storyId = args.storyId!! @@ -97,7 +107,8 @@ class StateFragment : internalProfileId, topicId, storyId, - explorationId + explorationId, + userAnswerState ) } @@ -146,4 +157,12 @@ class StateFragment : fun dismissConceptCard() = stateFragmentPresenter.dismissConceptCard() fun getExplorationCheckpointState() = stateFragmentPresenter.getExplorationCheckpointState() + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putProto( + STATE_FRAGMENT_USER_ANSWER_STATE_KEY, + stateFragmentPresenter.getUserAnswerState() + ) + } } diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt index aea996137c5..c84474cc9c1 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt @@ -54,6 +54,7 @@ import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.ExplorationHtmlParserEntityType import org.oppia.android.util.system.OppiaClock import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState const val STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_fragment_profile_id" @@ -111,7 +112,8 @@ class StateFragmentPresenter @Inject constructor( internalProfileId: Int, topicId: String, storyId: String, - explorationId: String + explorationId: String, + userAnswerState: UserAnswerState ): View? { profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() this.topicId = topicId @@ -125,7 +127,7 @@ class StateFragmentPresenter @Inject constructor( /* attachToRoot= */ false ) recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, entityType, profileId), + assemblerBuilderFactory.create(resourceBucketName, entityType, profileId,userAnswerState), binding.congratulationsTextView, binding.congratulationsTextConfettiView, binding.fullScreenConfettiView @@ -365,6 +367,9 @@ class StateFragmentPresenter @Inject constructor( private fun subscribeToAnswerOutcome( answerOutcomeResultLiveData: LiveData> ) { + if (stateViewModel.getCanSubmitAnswer().get()==true){ + recyclerViewAssembler.resetUserAnswerState() + } val answerOutcomeLiveData = getAnswerOutcome(answerOutcomeResultLiveData) answerOutcomeLiveData.observe( fragment, @@ -385,6 +390,11 @@ class StateFragmentPresenter @Inject constructor( ) } + /** Returns the [UserAnswerState] representing the user's current pending answer. */ + fun getUserAnswerState(): UserAnswerState { + return stateViewModel.getUserAnswerState(recyclerViewAssembler::getPendingAnswerHandler) + } + /** Helper for subscribeToAnswerOutcome. */ private fun getAnswerOutcome( answerOutcome: LiveData> diff --git a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt index e0de8c91d58..bc8416eeb30 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt @@ -93,6 +93,7 @@ import org.oppia.android.util.accessibility.AccessibilityService import org.oppia.android.util.parser.html.HtmlParser import org.oppia.android.util.threading.BackgroundDispatcher import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState private typealias AudioUiManagerRetriever = () -> AudioUiManager? @@ -143,7 +144,8 @@ class StatePlayerRecyclerViewAssembler private constructor( backgroundCoroutineDispatcher: CoroutineDispatcher, private val hasConversationView: Boolean, private val resourceHandler: AppLanguageResourceHandler, - private val translationController: TranslationController + private val translationController: TranslationController, + private var userAnswerState: UserAnswerState ) : HtmlParser.CustomOppiaTagActionListener { /** * A list of view models corresponding to past view models that are hidden by default. These are @@ -323,10 +325,15 @@ class StatePlayerRecyclerViewAssembler private constructor( hasPreviousButton, isSplitView.get()!!, writtenTranslationContext, - timeToStartNoticeAnimationMs + timeToStartNoticeAnimationMs, + userAnswerState ) } + fun resetUserAnswerState() { + userAnswerState = UserAnswerState.getDefaultInstance() + } + private fun addContentItem( pendingItemList: MutableList, ephemeralState: EphemeralState, @@ -904,7 +911,8 @@ class StatePlayerRecyclerViewAssembler private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, - private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, + private val userAnswerState: UserAnswerState ) { private val adapterBuilder: BindableAdapter.MultiTypeBuilder) -> InteractionAnswerHandler? + ): UserAnswerState { + return retrieveAnswerHandler(getAnswerItemList())?.getUserAnswerState() + ?: UserAnswerState.getDefaultInstance() + } + private fun getPendingAnswerWithoutError( answerHandler: InteractionAnswerHandler? ): UserAnswer? { diff --git a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt index 591a01d10ce..0bdad875aed 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt @@ -1,6 +1,7 @@ package org.oppia.android.app.player.state.answerhandling import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.UserAnswerState /** * A handler for interaction answers. Handlers can either require an additional user action before the answer can be @@ -26,6 +27,11 @@ interface InteractionAnswerHandler { fun getPendingAnswer(): UserAnswer? { return null } + + fun getUserAnswerState():UserAnswerState{ + return UserAnswerState.getDefaultInstance() + } + } /** diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ContinueInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ContinueInteractionViewModel.kt index 2200ad829a1..c0a72b642b6 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ContinueInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ContinueInteractionViewModel.kt @@ -10,6 +10,7 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandle import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.player.state.listener.PreviousNavigationButtonListener import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState // For context: // https://github.com/oppia/oppia/blob/37285a/extensions/interactions/Continue/directives/oppia-interactive-continue.directive.ts @@ -57,7 +58,8 @@ class ContinueInteractionViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return ContinueInteractionViewModel( interactionAnswerReceiver, 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 d6f88ea9cec..812057f9f9f 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 @@ -25,6 +25,7 @@ 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.UserAnswerState /** Represents the type of errors that can be thrown by drag and drop sort interaction. */ enum class DragAndDropSortInteractionError(@StringRes private var error: Int?) { @@ -250,7 +251,8 @@ class DragAndDropSortInteractionViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return DragAndDropSortInteractionViewModel( entityId, diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt index 193248effe7..7af74976636 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt @@ -18,6 +18,7 @@ import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.math.FractionParser import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** [StateItemViewModel] for the fraction input interaction. */ class FractionInteractionViewModel private constructor( @@ -149,7 +150,8 @@ class FractionInteractionViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return FractionInteractionViewModel( interaction, diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt index 4ba33e25422..ae954a62a80 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt @@ -20,6 +20,7 @@ import org.oppia.android.app.utility.NamedRegionClickedEvent import org.oppia.android.app.utility.OnClickableAreaClickedListener import org.oppia.android.app.utility.RegionClickedEvent import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** [StateItemViewModel] for image region selection. */ class ImageRegionSelectionInteractionViewModel private constructor( @@ -192,7 +193,8 @@ class ImageRegionSelectionInteractionViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return ImageRegionSelectionInteractionViewModel( entityId, diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt index 06b6bb3a47c..690c7a91125 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt @@ -51,6 +51,7 @@ import org.oppia.android.util.math.toPlainText import org.oppia.android.util.math.toRawLatex import javax.inject.Inject import org.oppia.android.app.model.MathBinaryOperation.Operator as UnaryOperator +import org.oppia.android.app.model.UserAnswerState /** * [StateItemViewModel] for input for numeric expressions, algebraic expressions, and math @@ -241,7 +242,8 @@ class MathExpressionInteractionsViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return MathExpressionInteractionsViewModel( interaction, diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt index 04174714b4f..09ca487be72 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt @@ -15,6 +15,7 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandle import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.translation.AppLanguageResourceHandler import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** [StateItemViewModel] for the numeric input interaction. */ class NumericInputViewModel private constructor( @@ -112,7 +113,8 @@ class NumericInputViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return NumericInputViewModel( hasConversationView, diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt index f5c0f323bec..88ac2e3e850 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt @@ -19,6 +19,7 @@ import org.oppia.android.app.utility.toAccessibleAnswerString import org.oppia.android.domain.translation.TranslationController import org.oppia.android.util.math.toAnswerString import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** [StateItemViewModel] for the ratio expression input interaction. */ class RatioExpressionInputInteractionViewModel private constructor( @@ -148,7 +149,8 @@ class RatioExpressionInputInteractionViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return RatioExpressionInputInteractionViewModel( interaction, diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt index af2d7e0f6cc..7db0f6caad0 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.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.ObservableBoolean @@ -21,6 +22,8 @@ import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.viewmodel.ObservableArrayList import org.oppia.android.domain.translation.TranslationController import javax.inject.Inject +import org.oppia.android.app.model.ItemSelectionAnswerState +import org.oppia.android.app.model.UserAnswerState /** Corresponds to the type of input that should be used for an item selection interaction view. */ enum class SelectionItemInputType { @@ -49,7 +52,8 @@ class SelectionInteractionViewModel private constructor( val isSplitView: Boolean, val writtenTranslationContext: WrittenTranslationContext, private val translationController: TranslationController, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.SELECTION_INTERACTION), InteractionAnswerHandler { private val interactionId: String = interaction.id @@ -106,6 +110,22 @@ class SelectionInteractionViewModel private constructor( pendingAnswerError = null, inputAnswerAvailable = true ) + + if (userAnswerState.itemSelection.selectedIndexesCount != 0) { + userAnswerState.itemSelection.selectedIndexesList.forEach { selectedIndex -> + selectedItems += selectedIndex + updateIsAnswerAvailable() + choiceItems[selectedIndex].isAnswerSelected.set(true) + } + } + } + + override fun getUserAnswerState():UserAnswerState { + return UserAnswerState.newBuilder().apply { + this.itemSelection = ItemSelectionAnswerState.newBuilder().addAllSelectedIndexes( + selectedItems + ).build() + }.build() } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -140,7 +160,7 @@ class SelectionInteractionViewModel private constructor( * It checks the pending error for the current selection input, and correspondingly * updates the error string based on the specified error category. */ - override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { + override fun checkPendingAnswerError(category:AnswerErrorCategory): String? { pendingAnswerError = when (category) { AnswerErrorCategory.REAL_TIME -> null AnswerErrorCategory.SUBMIT_TIME -> @@ -267,7 +287,8 @@ class SelectionInteractionViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return SelectionInteractionViewModel( entityId, @@ -277,7 +298,8 @@ class SelectionInteractionViewModel private constructor( isSplitView, writtenTranslationContext, translationController, - resourceHandler + resourceHandler, + userAnswerState ) } } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/StateItemViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/StateItemViewModel.kt index 3d93249cf7c..418dec9383c 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/StateItemViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/StateItemViewModel.kt @@ -1,6 +1,7 @@ package org.oppia.android.app.player.state.itemviewmodel import org.oppia.android.app.model.Interaction +import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -58,7 +59,8 @@ abstract class StateItemViewModel(val viewType: ViewType) : ObservableViewModel( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState = UserAnswerState.getDefaultInstance() ): StateItemViewModel } } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt index 7685e3673b9..e76dbf35378 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt @@ -17,6 +17,7 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiv import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.domain.translation.TranslationController import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** [StateItemViewModel] for the text input interaction. */ class TextInputViewModel private constructor( @@ -134,7 +135,8 @@ class TextInputViewModel private constructor( hasPreviousButton: Boolean, isSplitView: Boolean, writtenTranslationContext: WrittenTranslationContext, - timeToStartNoticeAnimationMs: Long? + timeToStartNoticeAnimationMs: Long?, + userAnswerState: UserAnswerState ): StateItemViewModel { return TextInputViewModel( interaction, diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 24ec703f0a8..5c83bb3d053 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -420,3 +420,32 @@ enum CheckpointState { // Progress made in the exploration is not saved. CHECKPOINT_UNSAVED = 3; } + +// Corresponds to a item selection answer that user has selected. +message ItemSelectionAnswerState { + repeated int32 selected_indexes = 1; +} + +// Corresponds to a raw representation of the current answer entered by the user +// which is used to retain state on configuration changes +message UserAnswerState { + AnswerErrorCategory answer_error_category = 1; + oneof answer_input_type{ + ItemSelectionAnswerState item_Selection = 2; + } +} + +// Represents categories of errors that can be inferred from a pending answer. +enum AnswerErrorCategory { + // Corresponds to an unknown or unsupported error category. + ANSWER_ERROR_CATEGORY_UNSPECIFIED = 0; + + // Corresponds to the pending answer having zero errors. + NO_ERROR = 1; + + // Corresponds to errors that may be found while the user is trying to input an answer. + REAL_TIME = 2; + + // Corresponds to errors that may be found only when a user tries to submit an answer. + SUBMIT_TIME = 3; +} From 673ba946e2658eac1a6f4aabf7dea97c18c6b37c Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Sat, 6 Jul 2024 11:55:51 +0530 Subject: [PATCH 02/18] klint --- .../oppia/android/app/player/state/StateFragment.kt | 2 +- .../app/player/state/StateFragmentPresenter.kt | 8 ++++---- .../player/state/StatePlayerRecyclerViewAssembler.kt | 11 ++++++++--- .../oppia/android/app/player/state/StateViewModel.kt | 2 +- .../state/answerhandling/InteractionAnswerHandler.kt | 3 +-- .../itemviewmodel/ContinueInteractionViewModel.kt | 2 +- .../DragAndDropSortInteractionViewModel.kt | 2 +- .../itemviewmodel/FractionInteractionViewModel.kt | 2 +- .../ImageRegionSelectionInteractionViewModel.kt | 2 +- .../MathExpressionInteractionsViewModel.kt | 2 +- .../state/itemviewmodel/NumericInputViewModel.kt | 2 +- .../RatioExpressionInputInteractionViewModel.kt | 2 +- .../itemviewmodel/SelectionInteractionViewModel.kt | 11 +++++------ .../player/state/itemviewmodel/TextInputViewModel.kt | 2 +- 14 files changed, 28 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt index fa2230df8e1..44c3d333252 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt @@ -10,6 +10,7 @@ import org.oppia.android.app.fragment.InjectableFragment import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.StateFragmentArguments import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -23,7 +24,6 @@ import org.oppia.android.app.player.state.listener.SubmitNavigationButtonListene import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto import javax.inject.Inject -import org.oppia.android.app.model.UserAnswerState /** Fragment that represents the current state of an exploration. */ class StateFragment : diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt index c84474cc9c1..a0b73c6d137 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt @@ -27,6 +27,7 @@ import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.State import org.oppia.android.app.model.SurveyQuestionName import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.player.audio.AudioButtonListener import org.oppia.android.app.player.audio.AudioFragment import org.oppia.android.app.player.audio.AudioUiManager @@ -54,7 +55,6 @@ import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.ExplorationHtmlParserEntityType import org.oppia.android.util.system.OppiaClock import javax.inject.Inject -import org.oppia.android.app.model.UserAnswerState const val STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_fragment_profile_id" @@ -127,7 +127,7 @@ class StateFragmentPresenter @Inject constructor( /* attachToRoot= */ false ) recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, entityType, profileId,userAnswerState), + assemblerBuilderFactory.create(resourceBucketName, entityType, profileId, userAnswerState), binding.congratulationsTextView, binding.congratulationsTextConfettiView, binding.fullScreenConfettiView @@ -367,7 +367,7 @@ class StateFragmentPresenter @Inject constructor( private fun subscribeToAnswerOutcome( answerOutcomeResultLiveData: LiveData> ) { - if (stateViewModel.getCanSubmitAnswer().get()==true){ + if (stateViewModel.getCanSubmitAnswer().get() == true) { recyclerViewAssembler.resetUserAnswerState() } val answerOutcomeLiveData = getAnswerOutcome(answerOutcomeResultLiveData) @@ -392,7 +392,7 @@ class StateFragmentPresenter @Inject constructor( /** Returns the [UserAnswerState] representing the user's current pending answer. */ fun getUserAnswerState(): UserAnswerState { - return stateViewModel.getUserAnswerState(recyclerViewAssembler::getPendingAnswerHandler) + return stateViewModel.getUserAnswerState(recyclerViewAssembler::getPendingAnswerHandler) } /** Helper for subscribeToAnswerOutcome. */ diff --git a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt index bc8416eeb30..5dd9202c470 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt @@ -28,6 +28,7 @@ import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.StringList import org.oppia.android.app.model.SubtitledHtml import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.audio.AudioUiManager import org.oppia.android.app.player.state.StatePlayerRecyclerViewAssembler.Builder.Factory @@ -93,7 +94,6 @@ import org.oppia.android.util.accessibility.AccessibilityService import org.oppia.android.util.parser.html.HtmlParser import org.oppia.android.util.threading.BackgroundDispatcher import javax.inject.Inject -import org.oppia.android.app.model.UserAnswerState private typealias AudioUiManagerRetriever = () -> AudioUiManager? @@ -912,7 +912,7 @@ class StatePlayerRecyclerViewAssembler private constructor( private val translationController: TranslationController, private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, - private val userAnswerState: UserAnswerState + private val userAnswerState: UserAnswerState ) { private val adapterBuilder: BindableAdapter.MultiTypeBuilder null AnswerErrorCategory.SUBMIT_TIME -> diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt index e76dbf35378..6a62f05019c 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt @@ -9,6 +9,7 @@ import org.oppia.android.R import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver @@ -17,7 +18,6 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiv import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.domain.translation.TranslationController import javax.inject.Inject -import org.oppia.android.app.model.UserAnswerState /** [StateItemViewModel] for the text input interaction. */ class TextInputViewModel private constructor( From c64e91f5555b6d81df3c3926995243efcadf0db7 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 9 Jul 2024 08:41:24 +0530 Subject: [PATCH 03/18] fix checkbox state clearing when setting tint --- .../state/itemviewmodel/SelectionInteractionViewModel.kt | 4 +++- app/src/main/res/layout/item_selection_interaction_items.xml | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt index 68ebf63cf75..32c0e4a16ac 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt @@ -113,9 +113,11 @@ class SelectionInteractionViewModel private constructor( if (userAnswerState.itemSelection.selectedIndexesCount != 0) { userAnswerState.itemSelection.selectedIndexesList.forEach { selectedIndex -> selectedItems += selectedIndex - updateIsAnswerAvailable() choiceItems[selectedIndex].isAnswerSelected.set(true) } + updateItemSelectability() + updateSelectionText() + updateIsAnswerAvailable() } } diff --git a/app/src/main/res/layout/item_selection_interaction_items.xml b/app/src/main/res/layout/item_selection_interaction_items.xml index d94e5311ad1..ac4434d55d1 100755 --- a/app/src/main/res/layout/item_selection_interaction_items.xml +++ b/app/src/main/res/layout/item_selection_interaction_items.xml @@ -27,12 +27,13 @@ android:id="@+id/item_selection_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:checked="@{viewModel.answerSelected}" android:clickable="false" android:enabled="@{viewModel.isEnabled}" android:focusable="false" android:labelFor="@id/item_selection_contents_text_view" - app:buttonTint="@{viewModel.isEnabled ? @color/component_color_shared_item_selection_interaction_enabled_color : @color/component_color_shared_item_selection_interaction_disabled_color}" /> + app:buttonTint="@{viewModel.isEnabled ? @color/component_color_shared_item_selection_interaction_enabled_color : @color/component_color_shared_item_selection_interaction_disabled_color}" + android:checked="@{viewModel.answerSelected}" + /> Date: Tue, 9 Jul 2024 09:25:21 +0530 Subject: [PATCH 04/18] seletion state retain support in question player --- .../android/app/player/state/StateFragment.kt | 8 ++++---- .../questionplayer/QuestionPlayerFragment.kt | 19 ++++++++++++++++++- .../QuestionPlayerFragmentPresenter.kt | 14 ++++++++++++-- .../questionplayer/QuestionPlayerViewModel.kt | 8 ++++++++ .../item_selection_interaction_items.xml | 3 +-- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt index 44c3d333252..8b848ab3c44 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragment.kt @@ -43,8 +43,8 @@ class StateFragment : /** Arguments key for StateFragment. */ const val STATE_FRAGMENT_ARGUMENTS_KEY = "StateFragment.arguments" - /** Arguments key for StateFragment. */ - const val STATE_FRAGMENT_USER_ANSWER_STATE_KEY = "StateFragment.user_answer_state" + /** Arguments key for StateFragment saved state. */ + const val STATE_FRAGMENT_STATE_KEY = "StateFragment.state" /** * Creates a new instance of a StateFragment. @@ -92,7 +92,7 @@ class StateFragment : arguments?.getProto(STATE_FRAGMENT_ARGUMENTS_KEY, StateFragmentArguments.getDefaultInstance()) val userAnswerState = savedInstanceState?.getProto( - STATE_FRAGMENT_USER_ANSWER_STATE_KEY, + STATE_FRAGMENT_STATE_KEY, UserAnswerState.getDefaultInstance() ) ?: UserAnswerState.getDefaultInstance() @@ -161,7 +161,7 @@ class StateFragment : override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putProto( - STATE_FRAGMENT_USER_ANSWER_STATE_KEY, + STATE_FRAGMENT_STATE_KEY, stateFragmentPresenter.getUserAnswerState() ) } diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt index f92e0884e3d..180b15029b8 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt @@ -22,6 +22,8 @@ import org.oppia.android.app.player.state.listener.SubmitNavigationButtonListene import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState +import org.oppia.android.app.player.state.StateFragment /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : @@ -52,8 +54,12 @@ class QuestionPlayerFragment : val args = checkNotNull(arguments) { "Expected arguments to be passed to QuestionPlayerFragment" } + val userAnswerState = savedInstanceState?.getProto( + QUESTION_PLAYER_FRAGMENT_STATE_KEY, + UserAnswerState.getDefaultInstance() + ) ?: UserAnswerState.getDefaultInstance() val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) - return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId) + return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId,userAnswerState) } override fun onAnswerReadyForSubmission(answer: UserAnswer) { @@ -98,6 +104,9 @@ class QuestionPlayerFragment : companion object { private const val PROFILE_ID_ARGUMENT_KEY = "QuestionPlayerFragment.profile_id" + /** Arguments key for QuestionPlayerFragment saved state. */ + const val QUESTION_PLAYER_FRAGMENT_STATE_KEY = "QuestionPlayerFragment.state" + /** * Creates a new fragment to play a question session. * @@ -112,4 +121,12 @@ class QuestionPlayerFragment : } } } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putProto( + QUESTION_PLAYER_FRAGMENT_STATE_KEY, + questionPlayerFragmentPresenter.getUserAnswerState() + ) + } } diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt index 78a978ba51c..749ec8881e3 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt @@ -36,6 +36,7 @@ import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.gcsresource.QuestionResourceBucketName import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** The presenter for [QuestionPlayerFragment]. */ @FragmentScope @@ -69,7 +70,8 @@ class QuestionPlayerFragmentPresenter @Inject constructor( fun handleCreateView( inflater: LayoutInflater, container: ViewGroup?, - profileId: ProfileId + profileId: ProfileId, + userAnswerState: UserAnswerState ): View? { binding = QuestionPlayerFragmentBinding.inflate( inflater, @@ -79,7 +81,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( this.profileId = profileId recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, "skill", profileId), + assemblerBuilderFactory.create(resourceBucketName, "skill", profileId,userAnswerState), binding.congratulationsTextView, binding.congratulationsTextConfettiView ) @@ -154,6 +156,11 @@ class QuestionPlayerFragmentPresenter @Inject constructor( recyclerViewAssembler.adapter.notifyDataSetChanged() } + /** Returns the [UserAnswerState] representing the user's current pending answer. */ + fun getUserAnswerState(): UserAnswerState { + return questionViewModel.getUserAnswerState(recyclerViewAssembler::getPendingAnswerHandler) + } + /** * Updates whether the submit button should be active based on whether the pending answer is in an * error state. @@ -257,6 +264,9 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private fun subscribeToAnswerOutcome( answerOutcomeResultLiveData: LiveData> ) { + if (questionViewModel.getCanSubmitAnswer().get() == true) { + recyclerViewAssembler.resetUserAnswerState() + } val answerOutcomeLiveData = Transformations.map(answerOutcomeResultLiveData, ::processAnsweredQuestionOutcome) answerOutcomeLiveData.observe( diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt index ee470eb7b66..b51189d97a9 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt @@ -12,6 +12,7 @@ import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.viewmodel.ObservableArrayList import org.oppia.android.app.viewmodel.ObservableViewModel import javax.inject.Inject +import org.oppia.android.app.model.UserAnswerState /** [ObservableViewModel] for the question player. */ class QuestionPlayerViewModel @Inject constructor( @@ -92,6 +93,13 @@ class QuestionPlayerViewModel @Inject constructor( } } + fun getUserAnswerState( + retrieveAnswerHandler: (List) -> InteractionAnswerHandler? + ): UserAnswerState { + return retrieveAnswerHandler(getAnswerItemList())?.getUserAnswerState() + ?: UserAnswerState.getDefaultInstance() + } + private fun getPendingAnswerWithoutError( answerHandler: InteractionAnswerHandler? ): UserAnswer? { diff --git a/app/src/main/res/layout/item_selection_interaction_items.xml b/app/src/main/res/layout/item_selection_interaction_items.xml index ac4434d55d1..45aa17d6d2b 100755 --- a/app/src/main/res/layout/item_selection_interaction_items.xml +++ b/app/src/main/res/layout/item_selection_interaction_items.xml @@ -27,14 +27,13 @@ android:id="@+id/item_selection_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" + app:buttonTint="@{viewModel.isEnabled ? @color/component_color_shared_item_selection_interaction_enabled_color : @color/component_color_shared_item_selection_interaction_disabled_color}" android:clickable="false" android:enabled="@{viewModel.isEnabled}" android:focusable="false" android:labelFor="@id/item_selection_contents_text_view" - app:buttonTint="@{viewModel.isEnabled ? @color/component_color_shared_item_selection_interaction_enabled_color : @color/component_color_shared_item_selection_interaction_disabled_color}" android:checked="@{viewModel.answerSelected}" /> - Date: Tue, 9 Jul 2024 09:26:45 +0530 Subject: [PATCH 05/18] klint --- .../app/topic/questionplayer/QuestionPlayerFragment.kt | 7 ++++--- .../questionplayer/QuestionPlayerFragmentPresenter.kt | 4 ++-- .../app/topic/questionplayer/QuestionPlayerViewModel.kt | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt index 180b15029b8..e29d4ddf48e 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt @@ -10,6 +10,7 @@ import org.oppia.android.app.fragment.InjectableFragment import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.player.state.listener.ContinueNavigationButtonListener @@ -22,8 +23,6 @@ import org.oppia.android.app.player.state.listener.SubmitNavigationButtonListene import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto import javax.inject.Inject -import org.oppia.android.app.model.UserAnswerState -import org.oppia.android.app.player.state.StateFragment /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : @@ -59,7 +58,9 @@ class QuestionPlayerFragment : UserAnswerState.getDefaultInstance() ) ?: UserAnswerState.getDefaultInstance() val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) - return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId,userAnswerState) + return questionPlayerFragmentPresenter.handleCreateView( + inflater, container, profileId, userAnswerState + ) } override fun onAnswerReadyForSubmission(answer: UserAnswer) { diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt index 749ec8881e3..7d32e41cc8d 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragmentPresenter.kt @@ -20,6 +20,7 @@ import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.State import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.player.state.ConfettiConfig.MINI_CONFETTI_BURST import org.oppia.android.app.player.state.StatePlayerRecyclerViewAssembler import org.oppia.android.app.player.state.listener.RouteToHintsAndSolutionListener @@ -36,7 +37,6 @@ import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.gcsresource.QuestionResourceBucketName import javax.inject.Inject -import org.oppia.android.app.model.UserAnswerState /** The presenter for [QuestionPlayerFragment]. */ @FragmentScope @@ -81,7 +81,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( this.profileId = profileId recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, "skill", profileId,userAnswerState), + assemblerBuilderFactory.create(resourceBucketName, "skill", profileId, userAnswerState), binding.congratulationsTextView, binding.congratulationsTextConfettiView ) diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt index b51189d97a9..78e781e828b 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt @@ -5,6 +5,7 @@ import androidx.databinding.ObservableField import androidx.databinding.ObservableList import org.oppia.android.R import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel @@ -12,7 +13,6 @@ import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.viewmodel.ObservableArrayList import org.oppia.android.app.viewmodel.ObservableViewModel import javax.inject.Inject -import org.oppia.android.app.model.UserAnswerState /** [ObservableViewModel] for the question player. */ class QuestionPlayerViewModel @Inject constructor( From ea9ca3bcca58a699e543b05ff42ffce3e4954d34 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 9 Jul 2024 10:38:47 +0530 Subject: [PATCH 06/18] seletion state retain support with error --- .../android/app/player/state/StateViewModel.kt | 2 +- .../answerhandling/InteractionAnswerHandler.kt | 15 ++++++++------- .../DragAndDropSortInteractionViewModel.kt | 3 ++- .../itemviewmodel/FractionInteractionViewModel.kt | 2 +- .../ImageRegionSelectionInteractionViewModel.kt | 2 +- .../MathExpressionInteractionsViewModel.kt | 3 ++- .../state/itemviewmodel/NumericInputViewModel.kt | 3 ++- .../RatioExpressionInputInteractionViewModel.kt | 3 ++- .../SelectionInteractionViewModel.kt | 14 ++++++++++++-- .../state/itemviewmodel/TextInputViewModel.kt | 3 ++- .../FractionInputInteractionViewTestActivity.kt | 2 +- .../ImageRegionSelectionTestFragmentPresenter.kt | 2 +- .../testing/InputInteractionViewTestActivity.kt | 2 +- .../MathExpressionInteractionsViewTestActivity.kt | 2 +- .../RatioInputInteractionViewTestActivity.kt | 2 +- .../TextInputInteractionViewTestActivity.kt | 2 +- .../questionplayer/QuestionPlayerViewModel.kt | 2 +- .../MathExpressionInteractionsViewTest.kt | 4 ++-- 18 files changed, 42 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/StateViewModel.kt index f7e1f291e5f..b4350ce1baf 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/StateViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateViewModel.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.Observer import androidx.lifecycle.Transformations import androidx.lifecycle.ViewModel import org.oppia.android.app.fragment.FragmentScope +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.model.Profile @@ -15,7 +16,6 @@ import org.oppia.android.app.model.ProfileId import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationLanguageSelection -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel import org.oppia.android.app.viewmodel.ObservableArrayList diff --git a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt index ce9238a835b..93fff769c1b 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt @@ -1,5 +1,6 @@ package org.oppia.android.app.player.state.answerhandling +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState @@ -41,10 +42,10 @@ interface InteractionAnswerReceiver { fun onAnswerReadyForSubmission(answer: UserAnswer) } -/** Categories of errors that can be inferred from a pending answer. */ -enum class AnswerErrorCategory { - /** Corresponds to errors that may be found while the user is trying to input an answer. */ - REAL_TIME, - /** Corresponds to errors that may be found only when a user tries to submit an answer. */ - SUBMIT_TIME -} +// /** Categories of errors that can be inferred from a pending answer. */ +// enum class AnswerErrorCategory { +// /** Corresponds to errors that may be found while the user is trying to input an answer. */ +// REAL_TIME, +// /** Corresponds to errors that may be found only when a user tries to submit an answer. */ +// SUBMIT_TIME +// } 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 225910237d4..aa205417099 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 @@ -5,6 +5,7 @@ import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.recyclerview.widget.RecyclerView import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -16,7 +17,6 @@ import org.oppia.android.app.model.TranslatableHtmlContentId import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -164,6 +164,7 @@ class DragAndDropSortInteractionViewModel private constructor( AnswerErrorCategory.REAL_TIME -> null AnswerErrorCategory.SUBMIT_TIME -> getSubmitTimeError().getErrorMessageFromStringRes(resourceHandler) + else -> null } errorMessage.set(pendingAnswerError) return pendingAnswerError diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt index baaf5c837e9..e37d0cd1f14 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt @@ -5,13 +5,13 @@ import android.text.TextWatcher import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.parser.FractionParsingUiError -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt index e849060bd41..81af7f4d6d4 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.ClickOnImage import org.oppia.android.app.model.ImageWithRegions import org.oppia.android.app.model.Interaction @@ -11,7 +12,6 @@ import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt index f2d6c3c5acf..b3a9679a174 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt @@ -6,6 +6,7 @@ import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.MathEquation @@ -14,7 +15,6 @@ import org.oppia.android.app.model.OppiaLanguage import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -162,6 +162,7 @@ class MathExpressionInteractionsViewModel private constructor( answerText.toString(), allowedVariables, resourceHandler ) } + else -> { null } } errorMessage.set(pendingAnswerError) return pendingAnswerError diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt index c16b4cb3664..db6ddde9ff3 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt @@ -4,13 +4,13 @@ import android.text.Editable import android.text.TextWatcher import androidx.databinding.Observable import androidx.databinding.ObservableField +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.parser.StringToNumberParser -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -65,6 +65,7 @@ class NumericInputViewModel private constructor( AnswerErrorCategory.SUBMIT_TIME -> stringToNumberParser.getSubmitTimeError(answerText.toString()) .getErrorMessageFromStringRes(resourceHandler) + else -> null } errorMessage.set(pendingAnswerError) return pendingAnswerError diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt index 7b108c49535..be388ecd703 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt @@ -5,13 +5,13 @@ import android.text.TextWatcher import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.parser.StringToRatioParser -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -90,6 +90,7 @@ class RatioExpressionInputInteractionViewModel private constructor( answerText.toString(), numberOfTerms = numberOfTerms ).getErrorMessageFromStringRes(resourceHandler) + else -> null } errorMessage.set(pendingAnswerError) return pendingAnswerError diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt index 32c0e4a16ac..d5f4b7016c7 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt @@ -6,6 +6,7 @@ import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableField import androidx.databinding.ObservableList import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ItemSelectionAnswerState @@ -15,7 +16,6 @@ import org.oppia.android.app.model.TranslatableHtmlContentId import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -63,6 +63,9 @@ class SelectionInteractionViewModel private constructor( ?.map { schemaObject -> schemaObject.customSchemaValue.subtitledHtml } ?: listOf() } + + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR + private val minAllowableSelectionCount: Int by lazy { interaction.customizationArgsMap["minAllowableSelectionCount"]?.signedInt ?: 1 } @@ -119,6 +122,8 @@ class SelectionInteractionViewModel private constructor( updateSelectionText() updateIsAnswerAvailable() } + + checkPendingAnswerError(userAnswerState.answerErrorCategory) } override fun getUserAnswerState(): UserAnswerState { @@ -126,6 +131,7 @@ class SelectionInteractionViewModel private constructor( this.itemSelection = ItemSelectionAnswerState.newBuilder().addAllSelectedIndexes( selectedItems ).build() + this.answerErrorCategory = answerErrorCetegory }.build() } @@ -162,10 +168,14 @@ class SelectionInteractionViewModel private constructor( * updates the error string based on the specified error category. */ override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { + answerErrorCetegory = category pendingAnswerError = when (category) { - AnswerErrorCategory.REAL_TIME -> null + AnswerErrorCategory.REAL_TIME -> { + null + } AnswerErrorCategory.SUBMIT_TIME -> getSubmitTimeError().getErrorMessageFromStringRes(resourceHandler) + else -> null } errorMessage.set(pendingAnswerError) return pendingAnswerError diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt index 6a62f05019c..739c28811b5 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt @@ -6,12 +6,12 @@ import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -64,6 +64,7 @@ class TextInputViewModel private constructor( answerText.toString() ).createForText(resourceHandler) } + else -> { null } }.also { pendingAnswerError = it errorMessage.set(it) diff --git a/app/src/main/java/org/oppia/android/app/testing/FractionInputInteractionViewTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/FractionInputInteractionViewTestActivity.kt index 1e0a95cb7d9..86259ce5bbf 100644 --- a/app/src/main/java/org/oppia/android/app/testing/FractionInputInteractionViewTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/testing/FractionInputInteractionViewTestActivity.kt @@ -8,11 +8,11 @@ import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.customview.interaction.FractionInputInteractionView +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.InputInteractionViewTestActivityParams import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.player.state.itemviewmodel.FractionInteractionViewModel diff --git a/app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestFragmentPresenter.kt index cdd29071e40..b346a75ac56 100644 --- a/app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestFragmentPresenter.kt @@ -6,13 +6,13 @@ import android.view.ViewGroup import android.widget.Button import androidx.fragment.app.Fragment import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.ImageWithRegions.LabeledRegion import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.Point2d import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.ImageRegionSelectionInteractionView -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.player.state.itemviewmodel.ImageRegionSelectionInteractionViewModel diff --git a/app/src/main/java/org/oppia/android/app/testing/InputInteractionViewTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/InputInteractionViewTestActivity.kt index bda539ed689..506e89efd49 100644 --- a/app/src/main/java/org/oppia/android/app/testing/InputInteractionViewTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/testing/InputInteractionViewTestActivity.kt @@ -8,11 +8,11 @@ import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.customview.interaction.NumericInputInteractionView +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.InputInteractionViewTestActivityParams import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.player.state.itemviewmodel.NumericInputViewModel diff --git a/app/src/main/java/org/oppia/android/app/testing/MathExpressionInteractionsViewTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/MathExpressionInteractionsViewTestActivity.kt index aaa119915e3..620baeae186 100644 --- a/app/src/main/java/org/oppia/android/app/testing/MathExpressionInteractionsViewTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/testing/MathExpressionInteractionsViewTestActivity.kt @@ -7,12 +7,12 @@ import androidx.databinding.DataBindingUtil import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.MathExpressionInteractionsViewTestActivityParams import org.oppia.android.app.model.MathExpressionInteractionsViewTestActivityParams.MathInteractionType import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.player.state.itemviewmodel.MathExpressionInteractionsViewModel diff --git a/app/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity.kt index f307c69d318..348f0c14857 100644 --- a/app/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity.kt @@ -8,12 +8,12 @@ import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.customview.interaction.RatioInputInteractionView +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.InputInteractionViewTestActivityParams import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.SchemaObject import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.player.state.itemviewmodel.RatioExpressionInputInteractionViewModel diff --git a/app/src/main/java/org/oppia/android/app/testing/TextInputInteractionViewTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/TextInputInteractionViewTestActivity.kt index 2a175fb9429..a997d12a554 100644 --- a/app/src/main/java/org/oppia/android/app/testing/TextInputInteractionViewTestActivity.kt +++ b/app/src/main/java/org/oppia/android/app/testing/TextInputInteractionViewTestActivity.kt @@ -6,10 +6,10 @@ import org.oppia.android.R import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.customview.interaction.TextInputInteractionView +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt index 78e781e828b..4447e07bb80 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerViewModel.kt @@ -4,9 +4,9 @@ import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableField import androidx.databinding.ObservableList import org.oppia.android.R +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.UserAnswerState -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel import org.oppia.android.app.translation.AppLanguageResourceHandler diff --git a/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt b/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt index 68ed0efe096..3bf69aba46a 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt +++ b/app/src/sharedTest/java/org/oppia/android/app/customview/interaction/MathExpressionInteractionsViewTest.kt @@ -32,6 +32,8 @@ import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule +import org.oppia.android.app.model.AnswerErrorCategory.REAL_TIME +import org.oppia.android.app.model.AnswerErrorCategory.SUBMIT_TIME import org.oppia.android.app.model.CustomSchemaValue import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.MathExpressionInteractionsViewTestActivityParams @@ -42,8 +44,6 @@ import org.oppia.android.app.model.SchemaObjectList import org.oppia.android.app.model.SubtitledUnicode import org.oppia.android.app.model.Translation import org.oppia.android.app.model.WrittenTranslationContext -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory.REAL_TIME -import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory.SUBMIT_TIME import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule import org.oppia.android.app.shim.ViewBindingShimModule import org.oppia.android.app.testing.MathExpressionInteractionsViewTestActivity From 1a193a4be1a147abe26473b918d298502153e01a Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 9 Jul 2024 11:01:57 +0530 Subject: [PATCH 07/18] resolve warnings and static checks --- .../player/state/StatePlayerRecyclerViewAssembler.kt | 1 + .../state/answerhandling/InteractionAnswerHandler.kt | 8 -------- .../itemviewmodel/FractionInteractionViewModel.kt | 1 + .../ImageRegionSelectionInteractionViewModel.kt | 1 + .../MathExpressionInteractionsViewModel.kt | 2 +- .../player/state/itemviewmodel/TextInputViewModel.kt | 2 +- model/src/main/proto/exploration.proto | 11 ++++------- 7 files changed, 9 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt index 5dd9202c470..2804539dd78 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt @@ -330,6 +330,7 @@ class StatePlayerRecyclerViewAssembler private constructor( ) } + /** Reset userAnswerState once the user submits an answer. */ fun resetUserAnswerState() { userAnswerState = UserAnswerState.getDefaultInstance() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt index 93fff769c1b..d3b5224050d 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt @@ -41,11 +41,3 @@ interface InteractionAnswerHandler { interface InteractionAnswerReceiver { fun onAnswerReadyForSubmission(answer: UserAnswer) } - -// /** Categories of errors that can be inferred from a pending answer. */ -// enum class AnswerErrorCategory { -// /** Corresponds to errors that may be found while the user is trying to input an answer. */ -// REAL_TIME, -// /** Corresponds to errors that may be found only when a user tries to submit an answer. */ -// SUBMIT_TIME -// } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt index e37d0cd1f14..4e638b6a027 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt @@ -87,6 +87,7 @@ class FractionInteractionViewModel private constructor( fractionParser.getSubmitTimeError(answerText.toString()) ).getErrorMessageFromStringRes(resourceHandler) } + else -> {} } errorMessage.set(pendingAnswerError) return pendingAnswerError diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt index 81af7f4d6d4..aa56b9548e9 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt @@ -103,6 +103,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( ).getErrorMessageFromStringRes(resourceHandler) } } + else -> {} } errorMessage.set(pendingAnswerError) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt index b3a9679a174..1491261e5bb 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt @@ -162,7 +162,7 @@ class MathExpressionInteractionsViewModel private constructor( answerText.toString(), allowedVariables, resourceHandler ) } - else -> { null } + else -> null } errorMessage.set(pendingAnswerError) return pendingAnswerError diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt index 739c28811b5..522334c0db5 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt @@ -64,7 +64,7 @@ class TextInputViewModel private constructor( answerText.toString() ).createForText(resourceHandler) } - else -> { null } + else -> null }.also { pendingAnswerError = it errorMessage.set(it) diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 5c83bb3d053..803e9348898 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -431,21 +431,18 @@ message ItemSelectionAnswerState { message UserAnswerState { AnswerErrorCategory answer_error_category = 1; oneof answer_input_type{ - ItemSelectionAnswerState item_Selection = 2; + ItemSelectionAnswerState item_selection = 2; } } // Represents categories of errors that can be inferred from a pending answer. enum AnswerErrorCategory { - // Corresponds to an unknown or unsupported error category. - ANSWER_ERROR_CATEGORY_UNSPECIFIED = 0; - // Corresponds to the pending answer having zero errors. - NO_ERROR = 1; + NO_ERROR = 0; // Corresponds to errors that may be found while the user is trying to input an answer. - REAL_TIME = 2; + REAL_TIME = 1; // Corresponds to errors that may be found only when a user tries to submit an answer. - SUBMIT_TIME = 3; + SUBMIT_TIME = 2; } From 64af426bfd27f71b2df8bc14dbce37840e19d230 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 9 Jul 2024 11:16:40 +0530 Subject: [PATCH 08/18] proto lint fix --- .../state/itemviewmodel/SelectionInteractionViewModel.kt | 2 +- model/src/main/proto/exploration.proto | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt index d5f4b7016c7..fce0f3bcead 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt @@ -64,7 +64,7 @@ class SelectionInteractionViewModel private constructor( ?: listOf() } - private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED private val minAllowableSelectionCount: Int by lazy { interaction.customizationArgsMap["minAllowableSelectionCount"]?.signedInt ?: 1 diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 803e9348898..9e46a8bb7a6 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -437,8 +437,8 @@ message UserAnswerState { // Represents categories of errors that can be inferred from a pending answer. enum AnswerErrorCategory { - // Corresponds to the pending answer having zero errors. - NO_ERROR = 0; + // Corresponds to the pending answer having no error. + NO_ERROR_UNSPECIFIED = 0; // Corresponds to errors that may be found while the user is trying to input an answer. REAL_TIME = 1; From 3ccf7b4ecbdf6c6c43f3efa548579d5b93861491 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Wed, 10 Jul 2024 10:28:23 +0530 Subject: [PATCH 09/18] retain answer state in math,ratio, and numeric input interaction --- .../InteractionAnswerHandler.kt | 1 + .../FractionInteractionViewModel.kt | 18 +++++++++++++++--- .../MathExpressionInteractionsViewModel.kt | 19 ++++++++++++++++--- .../itemviewmodel/NumericInputViewModel.kt | 18 +++++++++++++++--- ...atioExpressionInputInteractionViewModel.kt | 18 +++++++++++++++--- .../state/itemviewmodel/TextInputViewModel.kt | 18 +++++++++++++++--- model/src/main/proto/exploration.proto | 3 +++ 7 files changed, 80 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt index d3b5224050d..9efcd4b83b0 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt @@ -29,6 +29,7 @@ interface InteractionAnswerHandler { return null } + /** Returns the current answer. */ fun getUserAnswerState(): UserAnswerState { return UserAnswerState.getDefaultInstance() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt index 4e638b6a027..4900b369341 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt @@ -28,10 +28,12 @@ class FractionInteractionViewModel private constructor( private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler, - private val translationController: TranslationController + private val translationController: TranslationController, + userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.FRACTION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = "" + var answerText: CharSequence = userAnswerState.textualAnswer + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") @@ -55,6 +57,7 @@ class FractionInteractionViewModel private constructor( /* pendingAnswerError= */null, /* inputAnswerAvailable= */true ) + checkPendingAnswerError(userAnswerState.answerErrorCategory) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -70,6 +73,7 @@ class FractionInteractionViewModel private constructor( /** It checks the pending error for the current fraction input, and correspondingly updates the error string based on the specified error category. */ override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { + answerErrorCetegory = category when (category) { AnswerErrorCategory.REAL_TIME -> { if (answerText.isNotEmpty()) { @@ -112,6 +116,13 @@ class FractionInteractionViewModel private constructor( } } + override fun getUserAnswerState(): UserAnswerState { + return UserAnswerState.newBuilder().apply { + this.textualAnswer = answerText.toString() + this.answerErrorCategory = answerErrorCetegory + }.build() + } + private fun deriveHintText(interaction: Interaction): CharSequence { // The subtitled unicode can apparently exist in the structure in two different formats. val placeholderUnicodeOption1 = @@ -161,7 +172,8 @@ class FractionInteractionViewModel private constructor( answerErrorReceiver, writtenTranslationContext, resourceHandler, - translationController + translationController, + userAnswerState ) } } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt index 1491261e5bb..8d2ca8eb69a 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt @@ -65,7 +65,8 @@ class MathExpressionInteractionsViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, private val mathExpressionAccessibilityUtil: MathExpressionAccessibilityUtil, - private val interactionType: InteractionType + private val interactionType: InteractionType, + userAnswerState: UserAnswerState ) : StateItemViewModel(interactionType.viewType), InteractionAnswerHandler { private var pendingAnswerError: String? = null @@ -73,7 +74,7 @@ class MathExpressionInteractionsViewModel private constructor( * Defines the current answer text being entered by the learner. This is expected to be directly * bound to the corresponding edit text. */ - var answerText: CharSequence = "" + var answerText: CharSequence = userAnswerState.textualAnswer // The value of ths field is set from the Binding and from the TextWatcher. Any // programmatic modification needs to be done here, so that the Binding and the TextWatcher // do not step on each other. @@ -81,6 +82,8 @@ class MathExpressionInteractionsViewModel private constructor( field = value.toString().trim() } + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED + /** * Defines whether an answer is currently available to parse. This is expected to be directly * bound to the UI. @@ -118,6 +121,14 @@ class MathExpressionInteractionsViewModel private constructor( pendingAnswerError = null, inputAnswerAvailable = true ) + checkPendingAnswerError(userAnswerState.answerErrorCategory) + } + + override fun getUserAnswerState(): UserAnswerState { + return UserAnswerState.newBuilder().apply { + this.textualAnswer = answerText.toString() + this.answerErrorCategory = answerErrorCetegory + }.build() } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -154,6 +165,7 @@ class MathExpressionInteractionsViewModel private constructor( }.build() override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { + answerErrorCetegory = category pendingAnswerError = when (category) { // There's no support for real-time errors. AnswerErrorCategory.REAL_TIME -> null @@ -254,7 +266,8 @@ class MathExpressionInteractionsViewModel private constructor( resourceHandler, translationController, mathExpressionAccessibilityUtil, - interactionType + interactionType, + userAnswerState ) } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt index db6ddde9ff3..bdcba83eedc 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt @@ -23,9 +23,11 @@ class NumericInputViewModel private constructor( private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, private val writtenTranslationContext: WrittenTranslationContext, - private val resourceHandler: AppLanguageResourceHandler + private val resourceHandler: AppLanguageResourceHandler, + userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.NUMERIC_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = "" + var answerText: CharSequence = userAnswerState.textualAnswer + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED private var pendingAnswerError: String? = null val errorMessage = ObservableField("") var isAnswerAvailable = ObservableField(false) @@ -49,6 +51,7 @@ class NumericInputViewModel private constructor( pendingAnswerError = null, inputAnswerAvailable = true ) + checkPendingAnswerError(userAnswerState.answerErrorCategory) } /** @@ -56,6 +59,7 @@ class NumericInputViewModel private constructor( * error string based on the specified error category. */ override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { + answerErrorCetegory = category pendingAnswerError = when (category) { AnswerErrorCategory.REAL_TIME -> if (answerText.isNotEmpty()) @@ -71,6 +75,13 @@ class NumericInputViewModel private constructor( return pendingAnswerError } + override fun getUserAnswerState(): UserAnswerState { + return UserAnswerState.newBuilder().apply { + this.textualAnswer = answerText.toString() + this.answerErrorCategory = answerErrorCetegory + }.build() + } + fun getAnswerTextWatcher(): TextWatcher { return object : TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { @@ -122,7 +133,8 @@ class NumericInputViewModel private constructor( answerErrorReceiver, isSplitView, writtenTranslationContext, - resourceHandler + resourceHandler, + userAnswerState ) } } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt index be388ecd703..05f6850a226 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt @@ -29,10 +29,12 @@ class RatioExpressionInputInteractionViewModel private constructor( private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler, - private val translationController: TranslationController + private val translationController: TranslationController, + userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.RATIO_EXPRESSION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = "" + var answerText: CharSequence = userAnswerState.textualAnswer + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") @@ -59,6 +61,7 @@ class RatioExpressionInputInteractionViewModel private constructor( pendingAnswerError = null, inputAnswerAvailable = true ) + checkPendingAnswerError(userAnswerState.answerErrorCategory) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -79,6 +82,7 @@ class RatioExpressionInputInteractionViewModel private constructor( * updates the error string based on the specified error category. */ override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { + answerErrorCetegory = category pendingAnswerError = when (category) { AnswerErrorCategory.REAL_TIME -> if (answerText.isNotEmpty()) @@ -96,6 +100,13 @@ class RatioExpressionInputInteractionViewModel private constructor( return pendingAnswerError } + override fun getUserAnswerState(): UserAnswerState { + return UserAnswerState.newBuilder().apply { + this.textualAnswer = answerText.toString() + this.answerErrorCategory = answerErrorCetegory + }.build() + } + fun getAnswerTextWatcher(): TextWatcher { return object : TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { @@ -160,7 +171,8 @@ class RatioExpressionInputInteractionViewModel private constructor( answerErrorReceiver, writtenTranslationContext, resourceHandler, - translationController + translationController, + userAnswerState ) } } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt index 522334c0db5..14cc21e21c2 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt @@ -27,9 +27,11 @@ class TextInputViewModel private constructor( val isSplitView: Boolean, private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler, - private val translationController: TranslationController + private val translationController: TranslationController, + userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = "" + var answerText: CharSequence = userAnswerState.textualAnswer + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED val hintText: CharSequence = deriveHintText(interaction) private var pendingAnswerError: String? = null @@ -54,9 +56,11 @@ class TextInputViewModel private constructor( pendingAnswerError = null, inputAnswerAvailable = true ) + checkPendingAnswerError(userAnswerState.answerErrorCategory) } override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { + answerErrorCetegory = category return when (category) { AnswerErrorCategory.REAL_TIME -> null AnswerErrorCategory.SUBMIT_TIME -> { @@ -101,6 +105,13 @@ class TextInputViewModel private constructor( } }.build() + override fun getUserAnswerState(): UserAnswerState { + return UserAnswerState.newBuilder().apply { + this.textualAnswer = answerText.toString() + this.answerErrorCategory = answerErrorCetegory + }.build() + } + private fun deriveHintText(interaction: Interaction): CharSequence { // The subtitled unicode can apparently exist in the structure in two different formats. val placeholderUnicodeOption1 = @@ -146,7 +157,8 @@ class TextInputViewModel private constructor( isSplitView, writtenTranslationContext, resourceHandler, - translationController + translationController, + userAnswerState ) } } diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 9e46a8bb7a6..7f6f9c5912a 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -432,6 +432,9 @@ message UserAnswerState { AnswerErrorCategory answer_error_category = 1; oneof answer_input_type{ ItemSelectionAnswerState item_selection = 2; + + // A raw answer entered by user in a text-based interactions. + string textual_answer = 3; } } From 4a84e3d6b19d23551d923a3b6cffb8101b165e8b Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Thu, 11 Jul 2024 10:28:30 +0530 Subject: [PATCH 10/18] Addressed comments --- .../player/state/StateFragmentPresenter.kt | 4 +-- .../state/StatePlayerRecyclerViewAssembler.kt | 2 +- .../FractionInteractionViewModel.kt | 6 ++--- .../MathExpressionInteractionsViewModel.kt | 6 ++--- .../itemviewmodel/NumericInputViewModel.kt | 6 ++--- ...atioExpressionInputInteractionViewModel.kt | 6 ++--- .../SelectionInteractionViewModel.kt | 2 +- .../state/itemviewmodel/TextInputViewModel.kt | 6 ++--- model/src/main/proto/exploration.proto | 26 ++++++++++++------- 9 files changed, 34 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt index a0b73c6d137..bd3e37c5bc4 100755 --- a/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StateFragmentPresenter.kt @@ -367,9 +367,7 @@ class StateFragmentPresenter @Inject constructor( private fun subscribeToAnswerOutcome( answerOutcomeResultLiveData: LiveData> ) { - if (stateViewModel.getCanSubmitAnswer().get() == true) { - recyclerViewAssembler.resetUserAnswerState() - } + recyclerViewAssembler.resetUserAnswerState() val answerOutcomeLiveData = getAnswerOutcome(answerOutcomeResultLiveData) answerOutcomeLiveData.observe( fragment, diff --git a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt index 2804539dd78..8f1e627d77a 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/StatePlayerRecyclerViewAssembler.kt @@ -1430,7 +1430,7 @@ class StatePlayerRecyclerViewAssembler private constructor( resourceBucketName: String, entityType: String, profileId: ProfileId, - userAnswerState: UserAnswerState = UserAnswerState.getDefaultInstance() + userAnswerState: UserAnswerState ): Builder { return Builder( accessibilityService, diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt index 4900b369341..b09059e8086 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt @@ -32,8 +32,8 @@ class FractionInteractionViewModel private constructor( userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.FRACTION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = userAnswerState.textualAnswer - private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED + var answerText: CharSequence = userAnswerState.textInputAnswer + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") @@ -118,7 +118,7 @@ class FractionInteractionViewModel private constructor( override fun getUserAnswerState(): UserAnswerState { return UserAnswerState.newBuilder().apply { - this.textualAnswer = answerText.toString() + this.textInputAnswer = answerText.toString() this.answerErrorCategory = answerErrorCetegory }.build() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt index 8d2ca8eb69a..9f6bd6682bf 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/MathExpressionInteractionsViewModel.kt @@ -74,7 +74,7 @@ class MathExpressionInteractionsViewModel private constructor( * Defines the current answer text being entered by the learner. This is expected to be directly * bound to the corresponding edit text. */ - var answerText: CharSequence = userAnswerState.textualAnswer + var answerText: CharSequence = userAnswerState.textInputAnswer // The value of ths field is set from the Binding and from the TextWatcher. Any // programmatic modification needs to be done here, so that the Binding and the TextWatcher // do not step on each other. @@ -82,7 +82,7 @@ class MathExpressionInteractionsViewModel private constructor( field = value.toString().trim() } - private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR /** * Defines whether an answer is currently available to parse. This is expected to be directly @@ -126,7 +126,7 @@ class MathExpressionInteractionsViewModel private constructor( override fun getUserAnswerState(): UserAnswerState { return UserAnswerState.newBuilder().apply { - this.textualAnswer = answerText.toString() + this.textInputAnswer = answerText.toString() this.answerErrorCategory = answerErrorCetegory }.build() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt index bdcba83eedc..347c2d5ccc7 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt @@ -26,8 +26,8 @@ class NumericInputViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.NUMERIC_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = userAnswerState.textualAnswer - private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED + var answerText: CharSequence = userAnswerState.textInputAnswer + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR private var pendingAnswerError: String? = null val errorMessage = ObservableField("") var isAnswerAvailable = ObservableField(false) @@ -77,7 +77,7 @@ class NumericInputViewModel private constructor( override fun getUserAnswerState(): UserAnswerState { return UserAnswerState.newBuilder().apply { - this.textualAnswer = answerText.toString() + this.textInputAnswer = answerText.toString() this.answerErrorCategory = answerErrorCetegory }.build() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt index 05f6850a226..215dfb811ff 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/RatioExpressionInputInteractionViewModel.kt @@ -33,8 +33,8 @@ class RatioExpressionInputInteractionViewModel private constructor( userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.RATIO_EXPRESSION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = userAnswerState.textualAnswer - private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED + var answerText: CharSequence = userAnswerState.textInputAnswer + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") @@ -102,7 +102,7 @@ class RatioExpressionInputInteractionViewModel private constructor( override fun getUserAnswerState(): UserAnswerState { return UserAnswerState.newBuilder().apply { - this.textualAnswer = answerText.toString() + this.textInputAnswer = answerText.toString() this.answerErrorCategory = answerErrorCetegory }.build() } diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt index fce0f3bcead..d5f4b7016c7 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt @@ -64,7 +64,7 @@ class SelectionInteractionViewModel private constructor( ?: listOf() } - private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR private val minAllowableSelectionCount: Int by lazy { interaction.customizationArgsMap["minAllowableSelectionCount"]?.signedInt ?: 1 diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt index 14cc21e21c2..88082ac39b3 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/TextInputViewModel.kt @@ -30,8 +30,8 @@ class TextInputViewModel private constructor( private val translationController: TranslationController, userAnswerState: UserAnswerState ) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = userAnswerState.textualAnswer - private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR_UNSPECIFIED + var answerText: CharSequence = userAnswerState.textInputAnswer + private var answerErrorCetegory: AnswerErrorCategory = AnswerErrorCategory.NO_ERROR val hintText: CharSequence = deriveHintText(interaction) private var pendingAnswerError: String? = null @@ -107,7 +107,7 @@ class TextInputViewModel private constructor( override fun getUserAnswerState(): UserAnswerState { return UserAnswerState.newBuilder().apply { - this.textualAnswer = answerText.toString() + this.textInputAnswer = answerText.toString() this.answerErrorCategory = answerErrorCetegory }.build() } diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 7f6f9c5912a..9c62edc2b9f 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -421,31 +421,37 @@ enum CheckpointState { CHECKPOINT_UNSAVED = 3; } -// Corresponds to a item selection answer that user has selected. +// Represents the state of item selection answers that the user has selected. message ItemSelectionAnswerState { + // List of selected item indexes repeated int32 selected_indexes = 1; } -// Corresponds to a raw representation of the current answer entered by the user -// which is used to retain state on configuration changes +// Represents the user's answer state, retaining state across configuration changes. message UserAnswerState { + // Represents the last error state. AnswerErrorCategory answer_error_category = 1; - oneof answer_input_type{ - ItemSelectionAnswerState item_selection = 2; - // A raw answer entered by user in a text-based interactions. - string textual_answer = 3; + // Type of answer input: + oneof answer_input_type { + // User's selection for selection input interactions. + ItemSelectionAnswerState item_selection = 2; + // Raw answer entered by the user in text-based interactions. + string text_input_answer = 3; } } // Represents categories of errors that can be inferred from a pending answer. enum AnswerErrorCategory { + // The error is unknown or not specified. + ERROR_UNSPECIFIED = 0; + // Corresponds to the pending answer having no error. - NO_ERROR_UNSPECIFIED = 0; + NO_ERROR = 1; // Corresponds to errors that may be found while the user is trying to input an answer. - REAL_TIME = 1; + REAL_TIME = 2; // Corresponds to errors that may be found only when a user tries to submit an answer. - SUBMIT_TIME = 2; + SUBMIT_TIME = 3; } From 454c320dff36b61af049ab6c50bad46a2d481182 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Fri, 12 Jul 2024 09:17:39 +0530 Subject: [PATCH 11/18] Addressed comments --- .../player/state/answerhandling/InteractionAnswerHandler.kt | 2 +- app/src/main/res/layout/item_selection_interaction_items.xml | 3 +-- model/src/main/proto/exploration.proto | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt index 9efcd4b83b0..5f7458efbc8 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/answerhandling/InteractionAnswerHandler.kt @@ -29,7 +29,7 @@ interface InteractionAnswerHandler { return null } - /** Returns the current answer. */ + /** Returns the current pending answer. */ fun getUserAnswerState(): UserAnswerState { return UserAnswerState.getDefaultInstance() } diff --git a/app/src/main/res/layout/item_selection_interaction_items.xml b/app/src/main/res/layout/item_selection_interaction_items.xml index 45aa17d6d2b..7fe2e6a5ba5 100755 --- a/app/src/main/res/layout/item_selection_interaction_items.xml +++ b/app/src/main/res/layout/item_selection_interaction_items.xml @@ -32,8 +32,7 @@ android:enabled="@{viewModel.isEnabled}" android:focusable="false" android:labelFor="@id/item_selection_contents_text_view" - android:checked="@{viewModel.answerSelected}" - /> + android:checked="@{viewModel.answerSelected}" /> Date: Tue, 30 Jul 2024 10:14:24 +0530 Subject: [PATCH 12/18] Added tests for all interactions saved input state --- app/build.gradle | 6 +- .../app/player/state/StateFragmentTest.kt | 378 +++++++++++++++++- 2 files changed, 378 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 13fae2de07a..fb2294b5232 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -197,7 +197,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.2.4', 'com.github.oppia:android-spotlight:cc23499d37dc8533a2876e45b5063e981a4583f4' ) compileOnly( @@ -221,7 +221,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.2.4', project(":testing"), ) androidTestImplementation( @@ -237,7 +237,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.2.4', '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 d7a9ae13b1e..68f13a2de73 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 @@ -30,10 +30,12 @@ import androidx.test.espresso.matcher.RootMatchers.isDialog import androidx.test.espresso.matcher.ViewMatchers.Visibility.GONE import androidx.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE import androidx.test.espresso.matcher.ViewMatchers.hasChildCount +import androidx.test.espresso.matcher.ViewMatchers.isChecked import androidx.test.espresso.matcher.ViewMatchers.isClickable import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isEnabled import androidx.test.espresso.matcher.ViewMatchers.isFocusable +import androidx.test.espresso.matcher.ViewMatchers.isNotChecked import androidx.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility @@ -193,6 +195,12 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton +import org.mockito.internal.matchers.Not +import org.oppia.android.app.customview.interaction.FractionInputInteractionView +import org.oppia.android.app.customview.interaction.MathExpressionInteractionsView +import org.oppia.android.app.customview.interaction.NumericInputInteractionView +import org.oppia.android.app.customview.interaction.RatioInputInteractionView +import org.oppia.android.app.customview.interaction.TextInputInteractionView /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -203,7 +211,7 @@ import javax.inject.Singleton @Suppress("FunctionName", "SameParameterValue") class StateFragmentTest { @get:Rule val initializeDefaultLocaleRule = InitializeDefaultLocaleRule() - @get:Rule val oppiaTestRule = OppiaTestRule() +// @get:Rule val oppiaTestRule = OppiaTestRule() @Inject lateinit var profileTestHelper: ProfileTestHelper @Inject lateinit var context: Context @@ -665,6 +673,8 @@ class StateFragmentTest { fun testStateFragment_loadExp_land_thirdState_selectAnswer_clickSubmit_continueIsVisible() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() rotateToLandscape() playThroughPrototypeState1() @@ -672,7 +682,8 @@ class StateFragmentTest { selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle") clickSubmitAnswerButton() - + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() scrollToViewType(CONTINUE_NAVIGATION_BUTTON) onView(withId(R.id.continue_navigation_button)).check( matches(withText(R.string.state_continue_button)) @@ -680,6 +691,367 @@ class StateFragmentTest { } } + @Test + fun testStateFragment_fractionInput_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + clickContinueInteractionButton() + // Entering text in Fraction Input Interaction. + typeFractionText("1/2") + // Rotating device. + rotateToLandscape() + onView(withId(R.id.fraction_input_interaction_view)).check(matches(withText("1/2"))) + } + } + + @Test + fun testStateFragment_numericInput_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + // Entering text in Numeric Input. + typeNumericInput("90") + // Rotating device. + rotateToLandscape() + onView(withId(R.id.numeric_input_interaction_view)).check(matches(withText("90"))) + } + } + + @Test + fun testStateFragment_ratioInput_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + playThroughPrototypeState6() + // Entering text in Ratio Input Interaction. + typeRatioExpression("3:5") + // Rotating device. + rotateToLandscape() + onView(withId(R.id.ratio_input_interaction_view)).check(matches(withText("3:5"))) + } + } + + @Test + fun testStateFragment_textInput_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + playThroughPrototypeState6() + playThroughPrototypeState7() + // Enter text in Text Input Interaction. + typeTextInput("finnish") + // Rotate the device. + rotateToLandscape() + onView(withId(R.id.text_input_interaction_view)).check(matches(withText("finnish"))) + } + } + + @Test + fun testStateFragment_selectMultipleChoiceOption_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + // Select answer. + selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle") + // Rotating device. + rotateToLandscape() + scrollToViewType(SELECTION_INTERACTION) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 2, + targetViewId = R.id.multiple_choice_radio_button + ) + ).check(matches(isChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 0, + targetViewId = R.id.multiple_choice_radio_button + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 1, + targetViewId = R.id.multiple_choice_radio_button + ) + ).check(matches(isNotChecked())) + } + } + + @Test + fun testStateFragment_selectItemSelectionCheckbox_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + // Select items. + selectItemSelectionCheckbox(optionPosition = 0, expectedOptionText = "Red") + selectItemSelectionCheckbox(optionPosition = 2, expectedOptionText = "Green") + selectItemSelectionCheckbox(optionPosition = 3, expectedOptionText = "Blue") + // Rotating device. + rotateToLandscape() + scrollToViewType(SELECTION_INTERACTION) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 0, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 2, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 3, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 1, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 4, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 5, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + } + } + + @Test + fun testStateFragment_mathInteractions_numericExp_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + // Enter text in numeric math input interaction. + typeNumericExpression("1+2") + // Rotating device. + rotateToLandscape() + onView(withId(R.id.math_expression_input_interaction_view)).check(matches(withText("1+2"))) + } + } + + @Test + fun testStateFragment_mathInteractions_algExp_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + playUpThroughMathInteractionExplorationState3() + typeAlgebraicExpression("x^2-x-2") + rotateToLandscape() + onView(withId(R.id.math_expression_input_interaction_view)).check( + matches(withText("x^2-x-2"))) + } + } + + @Test + fun testStateFragment_mathInteractions_mathEq_retainStateOnConfigurationChange() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + playUpThroughMathInteractionExplorationState6() + typeMathEquation("x^2-x-2=2y") + rotateToLandscape() + onView(withId(R.id.math_expression_input_interaction_view)).check( + matches(withText("x^2-x-2=2y"))) + } + } + + @Test + fun testStateFragment_differentSelectionInteractions_doesNotShareSavedInputState() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + + selectMultipleChoiceOption(optionPosition = 0, expectedOptionText = "Green") + // Rotate the device. + rotateToLandscape() + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 0, + targetViewId = R.id.multiple_choice_radio_button + ) + ).check(matches(isChecked())) + clickSubmitAnswerButton() + clickContinueNavigationButton() + // Ensure all checkboxes are unchecked, indicating no saved input state + // from previous interactions. + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 0, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 1, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 2, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 3, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 4, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 5, + targetViewId = R.id.item_selection_checkbox + ) + ).check(matches(isNotChecked())) + } + } + + @Test + fun testStateFragment_sameSelectionInteractions_doesNotShareSavedInputState() { + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + + playThroughPrototypeState1() + playThroughPrototypeState2() + + selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle") + rotateToLandscape() + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 2, + targetViewId = R.id.multiple_choice_radio_button + ) + ).check(matches(isChecked())) + clickSubmitAnswerButton() + clickContinueNavigationButton() + // Ensure all radio buttons are not selected, indicating no saved input state + // from previous interactions. + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 0, + targetViewId = R.id.multiple_choice_radio_button + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 1, + targetViewId = R.id.multiple_choice_radio_button + ) + ).check(matches(isNotChecked())) + onView( + atPositionOnView( + recyclerViewId = R.id.selection_interaction_recyclerview, + position = 2, + targetViewId = R.id.multiple_choice_radio_button + ) + ).check(matches(isNotChecked())) + } + } + + @Test + fun testStateFragment_mathExpBasedInteractions_doesNotShareSavedInputState(){ + setUpTestWithLanguageSwitchingFeatureOff() + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.unregisterIdlingResource() + startPlayingExploration() + typeNumericExpression("1+2") + rotateToLandscape() + onView(withId(R.id.math_expression_input_interaction_view)).check(matches(withText("1+2"))) + clickSubmitAnswerButton() + clickContinueNavigationButton() + // Ensure empty input, indicating no saved input state from previous interactions. + onView(withId(R.id.math_expression_input_interaction_view)).check(matches(withText(""))) + } + } + @Test fun testStateFragment_loadExp_thirdState_submitInvalidAnswer_submitButtonIsEnabled() { setUpTestWithLanguageSwitchingFeatureOff() @@ -4832,7 +5204,7 @@ class StateFragmentTest { private fun rotateToLandscape() { onView(isRoot()).perform(orientationLandscape()) - testCoroutineDispatchers.runCurrent() +// testCoroutineDispatchers.runCurrent() } private fun clickContinueInteractionButton() { From 128357edc17e379e7dab272d1d401a5858269cf5 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 30 Jul 2024 10:15:26 +0530 Subject: [PATCH 13/18] klint --- .../android/app/player/state/StateFragmentTest.kt | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) 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 68f13a2de73..4d10d326e6f 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 @@ -152,7 +152,6 @@ import org.oppia.android.domain.translation.TranslationController import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule import org.oppia.android.testing.BuildEnvironment import org.oppia.android.testing.FakeAnalyticsEventLogger -import org.oppia.android.testing.OppiaTestRule import org.oppia.android.testing.RunOn import org.oppia.android.testing.TestImageLoaderModule import org.oppia.android.testing.TestLogReportingModule @@ -195,12 +194,6 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton -import org.mockito.internal.matchers.Not -import org.oppia.android.app.customview.interaction.FractionInputInteractionView -import org.oppia.android.app.customview.interaction.MathExpressionInteractionsView -import org.oppia.android.app.customview.interaction.NumericInputInteractionView -import org.oppia.android.app.customview.interaction.RatioInputInteractionView -import org.oppia.android.app.customview.interaction.TextInputInteractionView /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -897,7 +890,8 @@ class StateFragmentTest { typeAlgebraicExpression("x^2-x-2") rotateToLandscape() onView(withId(R.id.math_expression_input_interaction_view)).check( - matches(withText("x^2-x-2"))) + matches(withText("x^2-x-2")) + ) } } @@ -912,7 +906,8 @@ class StateFragmentTest { typeMathEquation("x^2-x-2=2y") rotateToLandscape() onView(withId(R.id.math_expression_input_interaction_view)).check( - matches(withText("x^2-x-2=2y"))) + matches(withText("x^2-x-2=2y")) + ) } } @@ -1036,7 +1031,7 @@ class StateFragmentTest { } @Test - fun testStateFragment_mathExpBasedInteractions_doesNotShareSavedInputState(){ + fun testStateFragment_mathExpBasedInteractions_doesNotShareSavedInputState() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { testCoroutineDispatchers.runCurrent() From 2a98efb6e599ab16861984cb78f8ae71030daf53 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 30 Jul 2024 10:23:22 +0530 Subject: [PATCH 14/18] clean up --- app/build.gradle | 6 +-- .../app/player/state/StateFragmentTest.kt | 48 ++----------------- 2 files changed, 6 insertions(+), 48 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index fb2294b5232..13fae2de07a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -197,7 +197,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.2.4', + 'org.mockito:mockito-core:2.7.22', 'com.github.oppia:android-spotlight:cc23499d37dc8533a2876e45b5063e981a4583f4' ) compileOnly( @@ -221,7 +221,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.2.4', + 'org.mockito:mockito-core:2.7.22', project(":testing"), ) androidTestImplementation( @@ -237,7 +237,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.2.4', + '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 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 4d10d326e6f..b34428d1c5a 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 @@ -688,13 +688,9 @@ class StateFragmentTest { fun testStateFragment_fractionInput_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() clickContinueInteractionButton() - // Entering text in Fraction Input Interaction. typeFractionText("1/2") - // Rotating device. rotateToLandscape() onView(withId(R.id.fraction_input_interaction_view)).check(matches(withText("1/2"))) } @@ -704,17 +700,13 @@ class StateFragmentTest { fun testStateFragment_numericInput_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() playThroughPrototypeState1() playThroughPrototypeState2() playThroughPrototypeState3() playThroughPrototypeState4() playThroughPrototypeState5() - // Entering text in Numeric Input. typeNumericInput("90") - // Rotating device. rotateToLandscape() onView(withId(R.id.numeric_input_interaction_view)).check(matches(withText("90"))) } @@ -724,8 +716,6 @@ class StateFragmentTest { fun testStateFragment_ratioInput_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() playThroughPrototypeState1() playThroughPrototypeState2() @@ -733,9 +723,7 @@ class StateFragmentTest { playThroughPrototypeState4() playThroughPrototypeState5() playThroughPrototypeState6() - // Entering text in Ratio Input Interaction. typeRatioExpression("3:5") - // Rotating device. rotateToLandscape() onView(withId(R.id.ratio_input_interaction_view)).check(matches(withText("3:5"))) } @@ -745,8 +733,6 @@ class StateFragmentTest { fun testStateFragment_textInput_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() playThroughPrototypeState1() playThroughPrototypeState2() @@ -755,9 +741,7 @@ class StateFragmentTest { playThroughPrototypeState5() playThroughPrototypeState6() playThroughPrototypeState7() - // Enter text in Text Input Interaction. typeTextInput("finnish") - // Rotate the device. rotateToLandscape() onView(withId(R.id.text_input_interaction_view)).check(matches(withText("finnish"))) } @@ -767,14 +751,10 @@ class StateFragmentTest { fun testStateFragment_selectMultipleChoiceOption_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() playThroughPrototypeState1() playThroughPrototypeState2() - // Select answer. selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle") - // Rotating device. rotateToLandscape() scrollToViewType(SELECTION_INTERACTION) onView( @@ -805,18 +785,14 @@ class StateFragmentTest { fun testStateFragment_selectItemSelectionCheckbox_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() playThroughPrototypeState1() playThroughPrototypeState2() playThroughPrototypeState3() playThroughPrototypeState4() - // Select items. selectItemSelectionCheckbox(optionPosition = 0, expectedOptionText = "Red") selectItemSelectionCheckbox(optionPosition = 2, expectedOptionText = "Green") selectItemSelectionCheckbox(optionPosition = 3, expectedOptionText = "Blue") - // Rotating device. rotateToLandscape() scrollToViewType(SELECTION_INTERACTION) onView( @@ -868,12 +844,8 @@ class StateFragmentTest { fun testStateFragment_mathInteractions_numericExp_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() - // Enter text in numeric math input interaction. typeNumericExpression("1+2") - // Rotating device. rotateToLandscape() onView(withId(R.id.math_expression_input_interaction_view)).check(matches(withText("1+2"))) } @@ -883,8 +855,6 @@ class StateFragmentTest { fun testStateFragment_mathInteractions_algExp_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() playUpThroughMathInteractionExplorationState3() typeAlgebraicExpression("x^2-x-2") @@ -899,8 +869,6 @@ class StateFragmentTest { fun testStateFragment_mathInteractions_mathEq_retainStateOnConfigurationChange() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() playUpThroughMathInteractionExplorationState6() typeMathEquation("x^2-x-2=2y") @@ -915,16 +883,11 @@ class StateFragmentTest { fun testStateFragment_differentSelectionInteractions_doesNotShareSavedInputState() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() - playThroughPrototypeState1() playThroughPrototypeState2() playThroughPrototypeState3() - selectMultipleChoiceOption(optionPosition = 0, expectedOptionText = "Green") - // Rotate the device. rotateToLandscape() onView( atPositionOnView( @@ -986,13 +949,9 @@ class StateFragmentTest { fun testStateFragment_sameSelectionInteractions_doesNotShareSavedInputState() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() - playThroughPrototypeState1() playThroughPrototypeState2() - selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle") rotateToLandscape() onView( @@ -1031,11 +990,9 @@ class StateFragmentTest { } @Test - fun testStateFragment_mathExpBasedInteractions_doesNotShareSavedInputState() { + fun testStateFragment_textBasedInteractions_doesNotShareSavedInputState() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() typeNumericExpression("1+2") rotateToLandscape() @@ -1047,6 +1004,7 @@ class StateFragmentTest { } } + @Test fun testStateFragment_loadExp_thirdState_submitInvalidAnswer_submitButtonIsEnabled() { setUpTestWithLanguageSwitchingFeatureOff() @@ -5199,7 +5157,7 @@ class StateFragmentTest { private fun rotateToLandscape() { onView(isRoot()).perform(orientationLandscape()) -// testCoroutineDispatchers.runCurrent() + testCoroutineDispatchers.runCurrent() } private fun clickContinueInteractionButton() { From 20c8ade3aae1da754adb0b08908323b98d535974 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 30 Jul 2024 10:23:48 +0530 Subject: [PATCH 15/18] klint --- .../java/org/oppia/android/app/player/state/StateFragmentTest.kt | 1 - 1 file changed, 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 b34428d1c5a..cbe5f155461 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 @@ -1004,7 +1004,6 @@ class StateFragmentTest { } } - @Test fun testStateFragment_loadExp_thirdState_submitInvalidAnswer_submitButtonIsEnabled() { setUpTestWithLanguageSwitchingFeatureOff() From 9a8cc05ef26b6a76b3ba4bfd3a8cf019c31574b4 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 30 Jul 2024 11:29:30 +0530 Subject: [PATCH 16/18] Removed changes made for running tests locally --- .../org/oppia/android/app/player/state/StateFragmentTest.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 cbe5f155461..cfad9f5be33 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 @@ -204,7 +204,7 @@ import javax.inject.Singleton @Suppress("FunctionName", "SameParameterValue") class StateFragmentTest { @get:Rule val initializeDefaultLocaleRule = InitializeDefaultLocaleRule() -// @get:Rule val oppiaTestRule = OppiaTestRule() + @get:Rule val oppiaTestRule = OppiaTestRule() @Inject lateinit var profileTestHelper: ProfileTestHelper @Inject lateinit var context: Context @@ -666,8 +666,6 @@ class StateFragmentTest { fun testStateFragment_loadExp_land_thirdState_selectAnswer_clickSubmit_continueIsVisible() { setUpTestWithLanguageSwitchingFeatureOff() launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() startPlayingExploration() rotateToLandscape() playThroughPrototypeState1() @@ -675,8 +673,6 @@ class StateFragmentTest { selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle") clickSubmitAnswerButton() - testCoroutineDispatchers.runCurrent() - testCoroutineDispatchers.unregisterIdlingResource() scrollToViewType(CONTINUE_NAVIGATION_BUTTON) onView(withId(R.id.continue_navigation_button)).check( matches(withText(R.string.state_continue_button)) From ee784ce1e9e3a11752a2361dd26cd1639c7e8600 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Tue, 30 Jul 2024 11:45:23 +0530 Subject: [PATCH 17/18] Import OppiaTestRule --- .../java/org/oppia/android/app/player/state/StateFragmentTest.kt | 1 + 1 file changed, 1 insertion(+) 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 cfad9f5be33..eb141d43015 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 @@ -152,6 +152,7 @@ import org.oppia.android.domain.translation.TranslationController import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule import org.oppia.android.testing.BuildEnvironment import org.oppia.android.testing.FakeAnalyticsEventLogger +import org.oppia.android.testing.OppiaTestRule import org.oppia.android.testing.RunOn import org.oppia.android.testing.TestImageLoaderModule import org.oppia.android.testing.TestLogReportingModule From 811b33519592878cce5ce9ddab9ec1c2fe19c2b7 Mon Sep 17 00:00:00 2001 From: Vishwajith-Shettigar Date: Fri, 2 Aug 2024 10:08:21 +0530 Subject: [PATCH 18/18] Klint --- .../app/topic/questionplayer/QuestionPlayerFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt index ee50bad8b69..b5e67ec1318 100644 --- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt +++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerFragment.kt @@ -64,7 +64,9 @@ class QuestionPlayerFragment : val arguments = args.getProto(ARGUMENTS_KEY, QuestionPlayerFragmentArguments.getDefaultInstance()) val profileId = arguments.profileId - return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId,userAnswerState) + return questionPlayerFragmentPresenter.handleCreateView( + inflater, container, profileId, userAnswerState + ) } override fun onAnswerReadyForSubmission(answer: UserAnswer) {