From 9a7da9a123960ea8fb8102e170d7382ddae5cbe1 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 19 Oct 2022 22:36:08 +0530 Subject: [PATCH 01/76] RawUserAnswer added --- .../FractionInputInteractionView.kt | 1 + .../android/app/player/state/StateFragment.kt | 17 ++++++++++ .../player/state/StateFragmentPresenter.kt | 25 ++++++++++---- .../app/player/state/StateViewModel.kt | 24 ++++++++++++++ .../InteractionAnswerHandler.kt | 9 +++++ .../ContinueInteractionViewModel.kt | 9 +++++ .../DragAndDropSortInteractionViewModel.kt | 9 +++++ .../FractionInteractionViewModel.kt | 13 +++++++- ...mageRegionSelectionInteractionViewModel.kt | 9 +++++ .../MathExpressionInteractionsViewModel.kt | 11 ++++++- .../itemviewmodel/NumericInputViewModel.kt | 9 +++++ ...atioExpressionInputInteractionViewModel.kt | 9 +++++ .../SelectionInteractionViewModel.kt | 9 +++++ .../state/itemviewmodel/TextInputViewModel.kt | 9 +++++ model/src/main/proto/exploration.proto | 33 +++++++++++++++++++ 15 files changed, 188 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt b/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt index 1fb50be3f5b..87fc8b63857 100644 --- a/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.customview.interaction import android.content.Context import android.graphics.Typeface import android.util.AttributeSet +import android.util.Log import android.view.KeyEvent import android.view.KeyEvent.ACTION_UP import android.view.KeyEvent.KEYCODE_BACK 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 e55b81ef35d..4ebf207dfd5 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 @@ -8,6 +8,7 @@ import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment import org.oppia.android.app.model.HelpIndex +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler @@ -19,7 +20,9 @@ import org.oppia.android.app.player.state.listener.PreviousResponsesHeaderClickL import org.oppia.android.app.player.state.listener.ReturnToTopicNavigationButtonListener import org.oppia.android.app.player.state.listener.ShowHintAvailabilityListener import org.oppia.android.app.player.state.listener.SubmitNavigationButtonListener +import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.getStringFromBundle +import org.oppia.android.util.extensions.putProto import javax.inject.Inject /** Fragment that represents the current state of an exploration. */ @@ -79,12 +82,17 @@ class StateFragment : val storyId = arguments!!.getStringFromBundle(STATE_FRAGMENT_STORY_ID_ARGUMENT_KEY)!! val explorationId = arguments!!.getStringFromBundle(STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY)!! + var rawUserAnswer: RawUserAnswer? = null + if (savedInstanceState != null) { + rawUserAnswer = savedInstanceState.getProto("Answer", RawUserAnswer.getDefaultInstance()) + } return stateFragmentPresenter.handleCreateView( inflater, container, internalProfileId, topicId, storyId, + rawUserAnswer, explorationId ) } @@ -129,9 +137,18 @@ class StateFragment : stateFragmentPresenter.revealHint(hintIndex) } + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putProto("Answer", stateFragmentPresenter.handleOnSavedInstance()) + } + fun revealSolution() = stateFragmentPresenter.revealSolution() fun dismissConceptCard() = stateFragmentPresenter.dismissConceptCard() fun getExplorationCheckpointState() = stateFragmentPresenter.getExplorationCheckpointState() + + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } } 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 1f9a6116436..2c27c859e43 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 @@ -1,6 +1,7 @@ package org.oppia.android.app.player.state import android.content.Context +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -24,6 +25,7 @@ import org.oppia.android.app.model.CheckpointState import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.State import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.audio.AudioButtonListener @@ -105,6 +107,7 @@ class StateFragmentPresenter @Inject constructor( internalProfileId: Int, topicId: String, storyId: String, + rawUserAnswer: RawUserAnswer?, explorationId: String ): View? { profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() @@ -158,7 +161,7 @@ class StateFragmentPresenter @Inject constructor( ) } - subscribeToCurrentState() + subscribeToCurrentState(rawUserAnswer) return binding.root } @@ -278,25 +281,25 @@ class StateFragmentPresenter @Inject constructor( return getAudioFragment() as? AudioUiManager } - private fun subscribeToCurrentState() { + private fun subscribeToCurrentState(rawUserAnswer: RawUserAnswer?) { ephemeralStateLiveData.observe( fragment, { result -> - processEphemeralStateResult(result) + processEphemeralStateResult(result, rawUserAnswer) } ) } - private fun processEphemeralStateResult(result: AsyncResult) { + private fun processEphemeralStateResult(result: AsyncResult, rawUserAnswer: RawUserAnswer?) { when (result) { is AsyncResult.Failure -> oppiaLogger.e("StateFragment", "Failed to retrieve ephemeral state", result.error) is AsyncResult.Pending -> {} // Display nothing until a valid result is available. - is AsyncResult.Success -> processEphemeralState(result.value) + is AsyncResult.Success -> processEphemeralState(result.value, rawUserAnswer) } } - private fun processEphemeralState(ephemeralState: EphemeralState) { + private fun processEphemeralState(ephemeralState: EphemeralState, rawUserAnswer: RawUserAnswer?) { explorationCheckpointState = ephemeralState.checkpointState val shouldSplit = splitScreenManager.shouldSplitScreen(ephemeralState.state.interaction.id) if (shouldSplit) { @@ -307,6 +310,12 @@ class StateFragmentPresenter @Inject constructor( viewModel.centerGuidelinePercentage.set(1f) } + Log.d("testAnswer", "Ephemeral State Called") + + if (rawUserAnswer != null) { + viewModel.setRawUserAnswer(rawUserAnswer, recyclerViewAssembler::getPendingAnswerHandler) + } + val isInNewState = ::currentStateName.isInitialized && currentStateName != ephemeralState.state.name @@ -451,6 +460,10 @@ class StateFragmentPresenter @Inject constructor( /** Returns the checkpoint state for the current exploration. */ fun getExplorationCheckpointState() = explorationCheckpointState + fun handleOnSavedInstance(): RawUserAnswer { + return viewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) + } + private fun markExplorationCompleted() { storyProgressController.recordCompletedChapter( profileId, 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 a46208966b1..700ba36b132 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 @@ -4,6 +4,7 @@ import androidx.databinding.ObservableField import androidx.databinding.ObservableList import androidx.lifecycle.ViewModel import org.oppia.android.app.fragment.FragmentScope +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler @@ -56,6 +57,23 @@ class StateViewModel @Inject constructor() : ObservableViewModel() { ) ?: UserAnswer.getDefaultInstance() } + fun getRawUserAnswer( + retrieveAnswerHandler: (List) -> InteractionAnswerHandler? + ): RawUserAnswer { + return getRawUserAnswerWithError( + retrieveAnswerHandler( + getAnswerItemList() + ) + ) ?: RawUserAnswer.getDefaultInstance() + } + + fun setRawUserAnswer( + rawUserAnswer: RawUserAnswer, + retrieveAnswerHandler: (List) -> InteractionAnswerHandler? + ) { + retrieveAnswerHandler(getAnswerItemList())?.setRawUserAnswer(rawUserAnswer) + } + private fun getPendingAnswerWithoutError( answerHandler: InteractionAnswerHandler? ): UserAnswer? { @@ -66,6 +84,12 @@ class StateViewModel @Inject constructor() : ObservableViewModel() { } } + private fun getRawUserAnswerWithError( + answerHandler: InteractionAnswerHandler? + ): RawUserAnswer? { + return answerHandler?.getRawUserAnswer() + } + private fun getAnswerItemList(): List { return if (isSplitView.get() == true) { rightItemList 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 cb8251b3295..9d0c938df81 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer /** @@ -26,6 +27,14 @@ interface InteractionAnswerHandler { fun getPendingAnswer(): UserAnswer? { return null } + + /** Sets [RawUserAnswer] which is entered by user. */ + fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) + + /** Return the last entered answer entered by user. */ + fun getRawUserAnswer(): RawUserAnswer? { + return null + } } /** 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 d79be12f560..abff06760f9 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 @@ -3,6 +3,7 @@ package org.oppia.android.app.player.state.itemviewmodel import androidx.fragment.app.Fragment import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver @@ -41,6 +42,14 @@ class ContinueInteractionViewModel private constructor( this.writtenTranslationContext = this@ContinueInteractionViewModel.writtenTranslationContext }.build() + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } + + override fun getRawUserAnswer(): RawUserAnswer? { + TODO("Not yet implemented") + } + fun handleButtonClicked() { interactionAnswerReceiver.onAnswerReadyForSubmission(getPendingAnswer()) } 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 1509d9a295b..2c3157eaa0d 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 @@ -7,6 +7,7 @@ import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings import org.oppia.android.app.model.ListOfSetsOfTranslatableHtmlContentIds +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds import org.oppia.android.app.model.StringList import org.oppia.android.app.model.SubtitledHtml @@ -129,6 +130,14 @@ class DragAndDropSortInteractionViewModel private constructor( this@DragAndDropSortInteractionViewModel.writtenTranslationContext }.build() + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } + + override fun getRawUserAnswer(): RawUserAnswer? { + TODO("Not yet implemented") + } + /** Returns an HTML list containing all of the HTML string elements as items in the list. */ private fun convertItemsToAnswer(htmlItems: List): ListOfSetsOfHtmlStrings { return ListOfSetsOfHtmlStrings.newBuilder() 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 54ee1566a38..f5c89132046 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 @@ -2,11 +2,13 @@ package org.oppia.android.app.player.state.itemviewmodel import android.text.Editable import android.text.TextWatcher +import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.parser.FractionParsingUiError @@ -33,7 +35,6 @@ class FractionInteractionViewModel private constructor( var answerText: CharSequence = "" var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") - val hintText: CharSequence = deriveHintText(interaction) private val fractionParser = FractionParser() @@ -84,6 +85,16 @@ class FractionInteractionViewModel private constructor( return pendingAnswerError } + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + Log.d("testAnswer", rawUserAnswer.fraction) + } + + override fun getRawUserAnswer(): RawUserAnswer? = RawUserAnswer.newBuilder().apply { + if(answerText.isNotEmpty()) { + fraction = answerText.toString() + } + }.build() + fun getAnswerTextWatcher(): TextWatcher { return object : TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { 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 47b6a5e4569..2125200b4e9 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 @@ -7,6 +7,7 @@ import org.oppia.android.app.model.ClickOnImage import org.oppia.android.app.model.ImageWithRegions import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver @@ -83,6 +84,14 @@ class ImageRegionSelectionInteractionViewModel private constructor( this@ImageRegionSelectionInteractionViewModel.writtenTranslationContext }.build() + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } + + override fun getRawUserAnswer(): RawUserAnswer? { + TODO("Not yet implemented") + } + private fun parseClickOnImage(answerTextString: String): ClickOnImage { val region = selectableRegions.find { it.label == answerTextString } return ClickOnImage.newBuilder() 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 77687c70a5d..5b1b4b3c85a 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 @@ -8,9 +8,11 @@ import androidx.databinding.ObservableField 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.MathBinaryOperation.Operator as UnaryOperator import org.oppia.android.app.model.MathEquation import org.oppia.android.app.model.MathExpression import org.oppia.android.app.model.OppiaLanguage +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory @@ -50,7 +52,6 @@ import org.oppia.android.util.math.MathParsingError.VariableInNumericExpressionE 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 /** * [StateItemViewModel] for input for numeric expressions, algebraic expressions, and math @@ -140,6 +141,14 @@ class MathExpressionInteractionsViewModel private constructor( } }.build() + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } + + override fun getRawUserAnswer(): RawUserAnswer? { + TODO("Not yet implemented") + } + override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { if (answerText.isNotEmpty()) { pendingAnswerError = when (category) { 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 70deff47224..df6ba909200 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 @@ -6,6 +6,7 @@ import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.parser.StringToNumberParser @@ -90,6 +91,14 @@ class NumericInputViewModel private constructor( } }.build() + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } + + override fun getRawUserAnswer(): RawUserAnswer? { + TODO("Not yet implemented") + } + /** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */ class FactoryImpl @Inject constructor( private val resourceHandler: AppLanguageResourceHandler 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 749151c4c40..3965058bdc8 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 @@ -7,6 +7,7 @@ import androidx.databinding.ObservableField 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.parser.StringToRatioParser @@ -67,6 +68,14 @@ class RatioExpressionInputInteractionViewModel private constructor( } }.build() + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } + + override fun getRawUserAnswer(): RawUserAnswer? { + TODO("Not yet implemented") + } + /** It checks the pending error for the current ratio input, and correspondingly updates the error string based on the specified error category. */ override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { if (answerText.isNotEmpty()) { 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 21976f2a971..5a466ea7fb5 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 @@ -5,6 +5,7 @@ import androidx.databinding.ObservableField import androidx.databinding.ObservableList import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds import org.oppia.android.app.model.SubtitledHtml import org.oppia.android.app.model.TranslatableHtmlContentId @@ -98,6 +99,14 @@ class SelectionInteractionViewModel private constructor( writtenTranslationContext = translationContext }.build() + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } + + override fun getRawUserAnswer(): RawUserAnswer? { + TODO("Not yet implemented") + } + /** Returns an HTML list containing all of the HTML string elements as items in the list. */ private fun convertSelectedItemsToHtmlString(itemHtmls: Collection): String { return when (itemHtmls.size) { 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 9395c23cdb7..b935ee1d40e 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 @@ -7,6 +7,7 @@ import androidx.databinding.ObservableField 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver @@ -73,6 +74,14 @@ class TextInputViewModel private constructor( } }.build() + override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { + TODO("Not yet implemented") + } + + override fun getRawUserAnswer(): RawUserAnswer? { + TODO("Not yet implemented") + } + private fun deriveHintText(interaction: Interaction): CharSequence { // The subtitled unicode can apparently exist in the structure in two different formats. val placeholderUnicodeOption1 = diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 7f8addd77c4..1dbc20c687f 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -322,6 +322,39 @@ message UserAnswer { WrittenTranslationContext written_translation_context = 6; } +// Corresponds to a raw item selection answer that user has submitted. +message ItemSelectionRawAnswer { + repeated int32 selected_indexes = 1; +} + +// Corresponds to a raw user answer that user has submitted. +message RawUserAnswer { + oneof answer_input_type { + // Just indicates the presence of the type. + bool continue = 1; + // A raw answer entered by user in TextInputInteraction. + string text = 2; + // A raw answer entered by user in FactionInputInteraction. + string fraction = 3; + // A raw answer entered by user in NumericInputInteraction. + string numeric = 4; + // A raw answer entered by user in NumericInputInteraction with units. + string number_with_units = 5; + // A raw answer entered by user in RatioInputInteraction. + string ratio_input = 6; + // A raw answer entered by user in MathInputInteraction with numeric expression. + string numeric_expression = 7; + // A raw answer entered by user in MathInputInteraction with algebraic expression. + string algebraic_expression = 8; + // A raw answer entered by user in MathInputInteraction with math equation. + string math_equation = 9; + int32 multiple_choice_selection_index = 10; + ItemSelectionRawAnswer item_selection = 11; + ListOfSetsOfTranslatableHtmlContentIds drag_and_drop = 12; + ClickOnImage image_region_selection = 13; + } +} + message AnswerAndResponse { // A previous answer the learner submitted. UserAnswer user_answer = 1; From 20280f1fb865612798e72c77c99ef5fa3f0c7c17 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 19 Oct 2022 22:38:13 +0530 Subject: [PATCH 02/76] Imports optimized --- .../customview/interaction/FractionInputInteractionView.kt | 1 - .../oppia/android/app/player/state/StateFragmentPresenter.kt | 5 ++++- .../state/itemviewmodel/FractionInteractionViewModel.kt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt b/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt index 87fc8b63857..1fb50be3f5b 100644 --- a/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/customview/interaction/FractionInputInteractionView.kt @@ -3,7 +3,6 @@ package org.oppia.android.app.customview.interaction import android.content.Context import android.graphics.Typeface import android.util.AttributeSet -import android.util.Log import android.view.KeyEvent import android.view.KeyEvent.ACTION_UP import android.view.KeyEvent.KEYCODE_BACK 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 2c27c859e43..8ad6245691e 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 @@ -290,7 +290,10 @@ class StateFragmentPresenter @Inject constructor( ) } - private fun processEphemeralStateResult(result: AsyncResult, rawUserAnswer: RawUserAnswer?) { + private fun processEphemeralStateResult( + result: AsyncResult, + rawUserAnswer: RawUserAnswer? + ) { when (result) { is AsyncResult.Failure -> oppiaLogger.e("StateFragment", "Failed to retrieve ephemeral state", result.error) 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 f5c89132046..548c2a129fa 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 @@ -90,7 +90,7 @@ class FractionInteractionViewModel private constructor( } override fun getRawUserAnswer(): RawUserAnswer? = RawUserAnswer.newBuilder().apply { - if(answerText.isNotEmpty()) { + if (answerText.isNotEmpty()) { fraction = answerText.toString() } }.build() From 3625a6384265533981d9db6543755ec27fe26c42 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 19 Oct 2022 22:39:44 +0530 Subject: [PATCH 03/76] Optimized import --- .../state/itemviewmodel/MathExpressionInteractionsViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5b1b4b3c85a..598fcad979e 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 @@ -8,7 +8,6 @@ import androidx.databinding.ObservableField 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.MathBinaryOperation.Operator as UnaryOperator import org.oppia.android.app.model.MathEquation import org.oppia.android.app.model.MathExpression import org.oppia.android.app.model.OppiaLanguage @@ -52,6 +51,7 @@ import org.oppia.android.util.math.MathParsingError.VariableInNumericExpressionE 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 /** * [StateItemViewModel] for input for numeric expressions, algebraic expressions, and math From 6f64dcc4a38ae3025e3deb19c09eb482c73d3f2d Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 21 Oct 2022 17:08:27 +0530 Subject: [PATCH 04/76] rawUserAnswer added to constructor of StateItemViewModel --- .../android/app/player/state/StateFragment.kt | 4 --- .../player/state/StateFragmentPresenter.kt | 5 +-- .../state/StatePlayerRecyclerViewAssembler.kt | 5 +++ .../app/player/state/StateViewModel.kt | 7 ---- .../InteractionAnswerHandler.kt | 3 -- .../ContinueInteractionViewModel.kt | 9 +++-- .../DragAndDropSortInteractionViewModel.kt | 11 +++--- .../FractionInteractionViewModel.kt | 11 +++--- ...mageRegionSelectionInteractionViewModel.kt | 16 +++++---- .../MathExpressionInteractionsViewModel.kt | 35 +++++++++++-------- .../itemviewmodel/NumericInputViewModel.kt | 20 +++++++---- ...atioExpressionInputInteractionViewModel.kt | 20 +++++++---- .../SelectionInteractionViewModel.kt | 13 ++++--- .../state/itemviewmodel/StateItemViewModel.kt | 2 ++ .../state/itemviewmodel/TextInputViewModel.kt | 18 ++++++---- .../InputInteractionViewTestActivity.kt | 1 + .../questionplayer/QuestionPlayerFragment.kt | 12 ++++++- .../QuestionPlayerFragmentPresenter.kt | 19 ++++++---- .../questionplayer/QuestionPlayerViewModel.kt | 17 +++++++++ model/src/main/proto/exploration.proto | 14 +++----- 20 files changed, 144 insertions(+), 98 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 4ebf207dfd5..df02137a5f9 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 @@ -147,8 +147,4 @@ class StateFragment : fun dismissConceptCard() = stateFragmentPresenter.dismissConceptCard() fun getExplorationCheckpointState() = stateFragmentPresenter.getExplorationCheckpointState() - - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } } 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 8ad6245691e..6305d9d03c2 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 @@ -315,10 +315,6 @@ class StateFragmentPresenter @Inject constructor( Log.d("testAnswer", "Ephemeral State Called") - if (rawUserAnswer != null) { - viewModel.setRawUserAnswer(rawUserAnswer, recyclerViewAssembler::getPendingAnswerHandler) - } - val isInNewState = ::currentStateName.isInitialized && currentStateName != ephemeralState.state.name @@ -330,6 +326,7 @@ class StateFragmentPresenter @Inject constructor( val dataPair = recyclerViewAssembler.compute( ephemeralState, explorationId, + rawUserAnswer, shouldSplit ) 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 bac406d7fda..e8b01b94283 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 @@ -94,6 +94,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.RawUserAnswer private typealias AudioUiManagerRetriever = () -> AudioUiManager? @@ -195,6 +196,7 @@ class StatePlayerRecyclerViewAssembler private constructor( fun compute( ephemeralState: EphemeralState, gcsEntityId: String, + rawUserAnswer: RawUserAnswer?, isSplitView: Boolean ): Pair, List> { this.isSplitView.set(isSplitView) @@ -231,6 +233,7 @@ class StatePlayerRecyclerViewAssembler private constructor( interaction, hasPreviousState, gcsEntityId, + rawUserAnswer, ephemeralState.writtenTranslationContext ) } @@ -305,12 +308,14 @@ class StatePlayerRecyclerViewAssembler private constructor( interaction: Interaction, hasPreviousButton: Boolean, gcsEntityId: String, + rawUserAnswer: RawUserAnswer?, writtenTranslationContext: WrittenTranslationContext ) { val interactionViewModelFactory = interactionViewModelFactoryMap.getValue(interaction.id) pendingItemList += interactionViewModelFactory.create( gcsEntityId, hasConversationView, + rawUserAnswer, interaction, fragment as InteractionAnswerReceiver, fragment as InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 700ba36b132..ec7e86f3e33 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 @@ -67,13 +67,6 @@ class StateViewModel @Inject constructor() : ObservableViewModel() { ) ?: RawUserAnswer.getDefaultInstance() } - fun setRawUserAnswer( - rawUserAnswer: RawUserAnswer, - retrieveAnswerHandler: (List) -> InteractionAnswerHandler? - ) { - retrieveAnswerHandler(getAnswerItemList())?.setRawUserAnswer(rawUserAnswer) - } - 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 9d0c938df81..9a7d8377623 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 @@ -28,9 +28,6 @@ interface InteractionAnswerHandler { return null } - /** Sets [RawUserAnswer] which is entered by user. */ - fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) - /** Return the last entered answer entered by user. */ fun getRawUserAnswer(): RawUserAnswer? { return null 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 abff06760f9..b9aaf5d1395 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 @@ -25,6 +25,7 @@ private const val DEFAULT_CONTINUE_INTERACTION_TEXT_ANSWER = "Please continue." class ContinueInteractionViewModel private constructor( private val interactionAnswerReceiver: InteractionAnswerReceiver, val hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, val hasPreviousButton: Boolean, val previousNavigationButtonListener: PreviousNavigationButtonListener, val isSplitView: Boolean, @@ -42,12 +43,8 @@ class ContinueInteractionViewModel private constructor( this.writtenTranslationContext = this@ContinueInteractionViewModel.writtenTranslationContext }.build() - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } - override fun getRawUserAnswer(): RawUserAnswer? { - TODO("Not yet implemented") + return RawUserAnswer.getDefaultInstance() } fun handleButtonClicked() { @@ -61,6 +58,7 @@ class ContinueInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -71,6 +69,7 @@ class ContinueInteractionViewModel private constructor( return ContinueInteractionViewModel( interactionAnswerReceiver, hasConversationView, + rawUserAnswer, hasPreviousButton, fragment as PreviousNavigationButtonListener, isSplitView, 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 2c3157eaa0d..0c62edc5f19 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 @@ -28,6 +28,7 @@ import javax.inject.Inject class DragAndDropSortInteractionViewModel private constructor( val entityId: String, val hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, @@ -130,12 +131,8 @@ class DragAndDropSortInteractionViewModel private constructor( this@DragAndDropSortInteractionViewModel.writtenTranslationContext }.build() - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } - - override fun getRawUserAnswer(): RawUserAnswer? { - TODO("Not yet implemented") + override fun getRawUserAnswer(): RawUserAnswer { + return RawUserAnswer.getDefaultInstance() } /** Returns an HTML list containing all of the HTML string elements as items in the list. */ @@ -207,6 +204,7 @@ class DragAndDropSortInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -217,6 +215,7 @@ class DragAndDropSortInteractionViewModel private constructor( return DragAndDropSortInteractionViewModel( entityId, hasConversationView, + rawUserAnswer, interaction, answerErrorReceiver, isSplitView, 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 548c2a129fa..3919e6485ad 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 @@ -24,6 +24,7 @@ import javax.inject.Inject /** [StateItemViewModel] for the fraction input interaction. */ class FractionInteractionViewModel private constructor( interaction: Interaction, + rawUserAnswer: RawUserAnswer?, val hasConversationView: Boolean, val isSplitView: Boolean, private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -48,8 +49,12 @@ class FractionInteractionViewModel private constructor( ) } } + if(rawUserAnswer!=null) { + answerText = rawUserAnswer.fraction + } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) + checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -85,10 +90,6 @@ class FractionInteractionViewModel private constructor( return pendingAnswerError } - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - Log.d("testAnswer", rawUserAnswer.fraction) - } - override fun getRawUserAnswer(): RawUserAnswer? = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { fraction = answerText.toString() @@ -147,6 +148,7 @@ class FractionInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -156,6 +158,7 @@ class FractionInteractionViewModel private constructor( ): StateItemViewModel { return FractionInteractionViewModel( interaction, + rawUserAnswer, hasConversationView, isSplitView, answerErrorReceiver, 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 2125200b4e9..32a5db5c080 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 @@ -24,6 +24,7 @@ import javax.inject.Inject class ImageRegionSelectionInteractionViewModel private constructor( val entityId: String, val hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, val isSplitView: Boolean, @@ -84,13 +85,12 @@ class ImageRegionSelectionInteractionViewModel private constructor( this@ImageRegionSelectionInteractionViewModel.writtenTranslationContext }.build() - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } - - override fun getRawUserAnswer(): RawUserAnswer? { - TODO("Not yet implemented") - } + override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { + if (answerText.isNotEmpty()) { + val answerTextString = answerText.toString() + imageRegionSelection = parseClickOnImage(answerTextString) + } + }.build() private fun parseClickOnImage(answerTextString: String): ClickOnImage { val region = selectableRegions.find { it.label == answerTextString } @@ -107,6 +107,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -117,6 +118,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( return ImageRegionSelectionInteractionViewModel( entityId, hasConversationView, + rawUserAnswer, interaction, answerErrorReceiver, isSplitView, 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 598fcad979e..3c7f0e653ea 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 @@ -2,6 +2,7 @@ package org.oppia.android.app.player.state.itemviewmodel import android.text.Editable import android.text.TextWatcher +import android.util.Log import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField @@ -60,6 +61,7 @@ import org.oppia.android.app.model.MathBinaryOperation.Operator as UnaryOperator class MathExpressionInteractionsViewModel private constructor( interaction: Interaction, val hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler, @@ -104,8 +106,13 @@ class MathExpressionInteractionsViewModel private constructor( ) } } + + if (rawUserAnswer!=null) { + answerText = rawUserAnswer.mathExpression + } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) + checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -124,30 +131,28 @@ class MathExpressionInteractionsViewModel private constructor( val mathContentValue = "{"raw_latex":"$answerAsLatex"}" htmlAnswer = "" + " math_content-with-value=\"$mathContentValue\" />" } else plainAnswer = answerTextString contentDescription = interactionType.computeHumanReadableString( - answerTextString, - useFractionsForDivision, - allowedVariables, - mathExpressionAccessibilityUtil, - this@MathExpressionInteractionsViewModel.writtenTranslationContext.language - ) ?: answerTextString + answerTextString, + useFractionsForDivision, + allowedVariables, + mathExpressionAccessibilityUtil, + this@MathExpressionInteractionsViewModel.writtenTranslationContext.language + ) ?: answerTextString this.writtenTranslationContext = this@MathExpressionInteractionsViewModel.writtenTranslationContext } }.build() - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } - - override fun getRawUserAnswer(): RawUserAnswer? { - TODO("Not yet implemented") - } + override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { + if (answerText.isNotEmpty()) { + mathExpression = answerText.toString() + } + }.build() override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { if (answerText.isNotEmpty()) { @@ -234,6 +239,7 @@ class MathExpressionInteractionsViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -244,6 +250,7 @@ class MathExpressionInteractionsViewModel private constructor( return MathExpressionInteractionsViewModel( interaction, hasConversationView, + rawUserAnswer, answerErrorReceiver, writtenTranslationContext, resourceHandler, 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 df6ba909200..f408003ba8a 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 @@ -20,6 +20,7 @@ import javax.inject.Inject /** [StateItemViewModel] for the numeric input interaction. */ class NumericInputViewModel private constructor( val hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, private val writtenTranslationContext: WrittenTranslationContext, @@ -41,8 +42,12 @@ class NumericInputViewModel private constructor( ) } } + if (rawUserAnswer!=null) { + answerText = rawUserAnswer.numeric + } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) + checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } /** It checks the pending error for the current numeric input, and correspondingly updates the error string based on the specified error category. */ @@ -91,13 +96,12 @@ class NumericInputViewModel private constructor( } }.build() - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } - - override fun getRawUserAnswer(): RawUserAnswer? { - TODO("Not yet implemented") - } + override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { + if(answerText.isNotEmpty()) { + val answerTextString = answerText.toString() + numeric = answerTextString + } + }.build() /** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */ class FactoryImpl @Inject constructor( @@ -106,6 +110,7 @@ class NumericInputViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -115,6 +120,7 @@ class NumericInputViewModel private constructor( ): StateItemViewModel { return NumericInputViewModel( hasConversationView, + rawUserAnswer, answerErrorReceiver, isSplitView, writtenTranslationContext, 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 3965058bdc8..90ea607c462 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 @@ -25,6 +25,7 @@ import javax.inject.Inject class RatioExpressionInputInteractionViewModel private constructor( interaction: Interaction, val hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, val isSplitView: Boolean, private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, private val writtenTranslationContext: WrittenTranslationContext, @@ -51,8 +52,13 @@ class RatioExpressionInputInteractionViewModel private constructor( ) } } + + if (rawUserAnswer!=null) { + answerText = rawUserAnswer.ratioInput + } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) + checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -68,13 +74,11 @@ class RatioExpressionInputInteractionViewModel private constructor( } }.build() - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } - - override fun getRawUserAnswer(): RawUserAnswer? { - TODO("Not yet implemented") - } + override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { + if (answerText.isNotEmpty()) { + ratioInput = answerText.toString() + } + }.build() /** It checks the pending error for the current ratio input, and correspondingly updates the error string based on the specified error category. */ override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { @@ -144,6 +148,7 @@ class RatioExpressionInputInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -154,6 +159,7 @@ class RatioExpressionInputInteractionViewModel private constructor( return RatioExpressionInputInteractionViewModel( interaction, hasConversationView, + rawUserAnswer, isSplitView, answerErrorReceiver, writtenTranslationContext, 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 5a466ea7fb5..3aab5e3cd52 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 @@ -28,6 +28,7 @@ enum class SelectionItemInputType { class SelectionInteractionViewModel private constructor( val entityId: String, val hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, @@ -99,13 +100,9 @@ class SelectionInteractionViewModel private constructor( writtenTranslationContext = translationContext }.build() - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } - - override fun getRawUserAnswer(): RawUserAnswer? { - TODO("Not yet implemented") - } + override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { + RawUserAnswer.getDefaultInstance() + }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ private fun convertSelectedItemsToHtmlString(itemHtmls: Collection): String { @@ -175,6 +172,7 @@ class SelectionInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -185,6 +183,7 @@ class SelectionInteractionViewModel private constructor( return SelectionInteractionViewModel( entityId, hasConversationView, + rawUserAnswer, interaction, answerErrorReceiver, isSplitView, 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 bce47761e47..479ad2d8a04 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.RawUserAnswer 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 @@ -47,6 +48,7 @@ abstract class StateItemViewModel(val viewType: ViewType) : ObservableViewModel( fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 b935ee1d40e..bfa23ab84ad 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 @@ -21,6 +21,7 @@ import javax.inject.Inject class TextInputViewModel private constructor( interaction: Interaction, val hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, private val writtenTranslationContext: WrittenTranslationContext, @@ -42,6 +43,9 @@ class TextInputViewModel private constructor( ) } } + if (rawUserAnswer!=null) { + answerText = rawUserAnswer.text + } isAnswerAvailable.addOnPropertyChangedCallback(callback) } @@ -74,13 +78,11 @@ class TextInputViewModel private constructor( } }.build() - override fun setRawUserAnswer(rawUserAnswer: RawUserAnswer) { - TODO("Not yet implemented") - } - - override fun getRawUserAnswer(): RawUserAnswer? { - TODO("Not yet implemented") - } + override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { + if (answerText.isNotEmpty()) { + text = answerText.toString() + } + }.build() private fun deriveHintText(interaction: Interaction): CharSequence { // The subtitled unicode can apparently exist in the structure in two different formats. @@ -111,6 +113,7 @@ class TextInputViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, + rawUserAnswer: RawUserAnswer?, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, @@ -121,6 +124,7 @@ class TextInputViewModel private constructor( return TextInputViewModel( interaction, hasConversationView, + rawUserAnswer, answerErrorReceiver, isSplitView, writtenTranslationContext, 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 1fc9c3d6042..8e06c97f75e 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 @@ -158,6 +158,7 @@ class InputInteractionViewTestActivity : return create( entityId = "fake_entity_id", hasConversationView = false, + rawUserAnswer = null, interaction = interaction, interactionAnswerReceiver = this@InputInteractionViewTestActivity, answerErrorReceiver = this@InputInteractionViewTestActivity, 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..c5d248c19a0 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,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.RawUserAnswer /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : @@ -52,8 +53,12 @@ class QuestionPlayerFragment : val args = checkNotNull(arguments) { "Expected arguments to be passed to QuestionPlayerFragment" } + var rawUserAnswer: RawUserAnswer? = null + if (savedInstanceState != null) { + rawUserAnswer = savedInstanceState.getProto("Answer", RawUserAnswer.getDefaultInstance()) + } val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) - return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId) + return questionPlayerFragmentPresenter.handleCreateView(inflater, container, rawUserAnswer, profileId) } override fun onAnswerReadyForSubmission(answer: UserAnswer) { @@ -83,6 +88,11 @@ class QuestionPlayerFragment : override fun onHintAvailable(helpIndex: HelpIndex, isCurrentStatePendingState: Boolean) = questionPlayerFragmentPresenter.onHintAvailable(helpIndex, isCurrentStatePendingState) + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putProto("Answer", questionPlayerFragmentPresenter.handleOnSavedInstance()) + } + fun handleKeyboardAction() = questionPlayerFragmentPresenter.handleKeyboardAction() fun revealHint(hintIndex: Int) { 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 596ee8287d8..bf15c31766c 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.RawUserAnswer /** The presenter for [QuestionPlayerFragment]. */ @FragmentScope @@ -68,6 +69,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( fun handleCreateView( inflater: LayoutInflater, container: ViewGroup?, + rawUserAnswer: RawUserAnswer?, profileId: ProfileId ): View? { binding = QuestionPlayerFragmentBinding.inflate( @@ -99,7 +101,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( helpIndex ) } - subscribeToCurrentQuestion() + subscribeToCurrentQuestion(rawUserAnswer) return binding.root } @@ -175,16 +177,16 @@ class QuestionPlayerFragmentPresenter @Inject constructor( showHintsAndSolutions(helpIndex, isCurrentStatePendingState) } - private fun subscribeToCurrentQuestion() { + private fun subscribeToCurrentQuestion(rawUserAnswer: RawUserAnswer?) { ephemeralQuestionLiveData.observe( fragment, Observer { - processEphemeralQuestionResult(it) + processEphemeralQuestionResult(it, rawUserAnswer) } ) } - private fun processEphemeralQuestionResult(result: AsyncResult) { + private fun processEphemeralQuestionResult(result: AsyncResult, rawUserAnswer: RawUserAnswer?) { when (result) { is AsyncResult.Failure -> { oppiaLogger.e( @@ -192,11 +194,11 @@ class QuestionPlayerFragmentPresenter @Inject constructor( ) } is AsyncResult.Pending -> {} // Display nothing until a valid result is available. - is AsyncResult.Success -> processEphemeralQuestion(result.value) + is AsyncResult.Success -> processEphemeralQuestion(result.value, rawUserAnswer) } } - private fun processEphemeralQuestion(ephemeralQuestion: EphemeralQuestion) { + private fun processEphemeralQuestion(ephemeralQuestion: EphemeralQuestion, rawUserAnswer: RawUserAnswer?) { // TODO(#497): Update this to properly link to question assets. val skillId = ephemeralQuestion.question.linkedSkillIdsList.firstOrNull() ?: "" @@ -225,6 +227,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( val dataPair = recyclerViewAssembler.compute( ephemeralQuestion.ephemeralState, skillId, + rawUserAnswer, isSplitView ) @@ -389,4 +392,8 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } } } + + fun handleOnSavedInstance(): RawUserAnswer { + return questionViewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) + } } 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..cb3e30b5e2e 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.RawUserAnswer /** [ObservableViewModel] for the question player. */ class QuestionPlayerViewModel @Inject constructor( @@ -102,6 +103,22 @@ class QuestionPlayerViewModel @Inject constructor( } } + fun getRawUserAnswer( + retrieveAnswerHandler: (List) -> InteractionAnswerHandler? + ): RawUserAnswer { + return getRawUserAnswerWithError( + retrieveAnswerHandler( + getAnswerItemList() + ) + ) ?: RawUserAnswer.getDefaultInstance() + } + + private fun getRawUserAnswerWithError( + answerHandler: InteractionAnswerHandler? + ): RawUserAnswer? { + return answerHandler?.getRawUserAnswer() + } + private fun getAnswerItemList(): List { return if (isSplitView.get() == true) { rightItemList diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 1dbc20c687f..942715dd81a 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -343,15 +343,11 @@ message RawUserAnswer { // A raw answer entered by user in RatioInputInteraction. string ratio_input = 6; // A raw answer entered by user in MathInputInteraction with numeric expression. - string numeric_expression = 7; - // A raw answer entered by user in MathInputInteraction with algebraic expression. - string algebraic_expression = 8; - // A raw answer entered by user in MathInputInteraction with math equation. - string math_equation = 9; - int32 multiple_choice_selection_index = 10; - ItemSelectionRawAnswer item_selection = 11; - ListOfSetsOfTranslatableHtmlContentIds drag_and_drop = 12; - ClickOnImage image_region_selection = 13; + string math_expression = 7; + int32 multiple_choice_selection_index = 8; + ItemSelectionRawAnswer item_selection = 9; + ListOfSetsOfTranslatableHtmlContentIds drag_and_drop = 10; + ClickOnImage image_region_selection = 11; } } From c4293c510a0e3e1f8613adc896c1337cc44e3514 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 21 Oct 2022 23:30:08 +0530 Subject: [PATCH 05/76] Platform parameter added --- .../player/state/StateFragmentPresenter.kt | 10 ++++- .../FractionInteractionViewModel.kt | 3 +- .../MathExpressionInteractionsViewModel.kt | 17 ++++--- .../itemviewmodel/NumericInputViewModel.kt | 4 +- ...atioExpressionInputInteractionViewModel.kt | 2 +- .../state/itemviewmodel/TextInputViewModel.kt | 4 +- .../questionplayer/QuestionPlayerFragment.kt | 6 ++- .../QuestionPlayerFragmentPresenter.kt | 20 +++++++-- .../questionplayer/QuestionPlayerViewModel.kt | 2 +- .../app/player/state/StateFragmentTest.kt | 44 ++++++++++++++----- 10 files changed, 79 insertions(+), 33 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 6305d9d03c2..ce4df8655e2 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 @@ -51,6 +51,8 @@ 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.util.platformparameter.EnableInteractionConfigChangeStateRetention +import org.oppia.android.util.platformparameter.PlatformParameterValue const val STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_fragment_profile_id" @@ -73,6 +75,8 @@ class StateFragmentPresenter @Inject constructor( private val storyProgressController: StoryProgressController, private val oppiaLogger: OppiaLogger, @DefaultResourceBucketName private val resourceBucketName: String, + @EnableInteractionConfigChangeStateRetention + private val isConfigChangeStateRetentionEnabled: PlatformParameterValue, private val assemblerBuilderFactory: StatePlayerRecyclerViewAssembler.Builder.Factory, private val splitScreenManager: SplitScreenManager, private val oppiaClock: OppiaClock @@ -461,7 +465,11 @@ class StateFragmentPresenter @Inject constructor( fun getExplorationCheckpointState() = explorationCheckpointState fun handleOnSavedInstance(): RawUserAnswer { - return viewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) + return if (isConfigChangeStateRetentionEnabled.value) { + viewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) + } else { + RawUserAnswer.getDefaultInstance() + } } private fun markExplorationCompleted() { 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 3919e6485ad..962b6b73b12 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 @@ -2,7 +2,6 @@ package org.oppia.android.app.player.state.itemviewmodel import android.text.Editable import android.text.TextWatcher -import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R @@ -49,7 +48,7 @@ class FractionInteractionViewModel private constructor( ) } } - if(rawUserAnswer!=null) { + if (rawUserAnswer != null) { answerText = rawUserAnswer.fraction } errorMessage.addOnPropertyChangedCallback(callback) 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 3c7f0e653ea..29e122066d8 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 @@ -2,7 +2,6 @@ package org.oppia.android.app.player.state.itemviewmodel import android.text.Editable import android.text.TextWatcher -import android.util.Log import androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableField @@ -107,7 +106,7 @@ class MathExpressionInteractionsViewModel private constructor( } } - if (rawUserAnswer!=null) { + if (rawUserAnswer != null) { answerText = rawUserAnswer.mathExpression } errorMessage.addOnPropertyChangedCallback(callback) @@ -131,17 +130,17 @@ class MathExpressionInteractionsViewModel private constructor( val mathContentValue = "{"raw_latex":"$answerAsLatex"}" htmlAnswer = "" + " math_content-with-value=\"$mathContentValue\" />" } else plainAnswer = answerTextString contentDescription = interactionType.computeHumanReadableString( - answerTextString, - useFractionsForDivision, - allowedVariables, - mathExpressionAccessibilityUtil, - this@MathExpressionInteractionsViewModel.writtenTranslationContext.language - ) ?: answerTextString + answerTextString, + useFractionsForDivision, + allowedVariables, + mathExpressionAccessibilityUtil, + this@MathExpressionInteractionsViewModel.writtenTranslationContext.language + ) ?: answerTextString this.writtenTranslationContext = this@MathExpressionInteractionsViewModel.writtenTranslationContext 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 f408003ba8a..d6034571715 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 @@ -42,7 +42,7 @@ class NumericInputViewModel private constructor( ) } } - if (rawUserAnswer!=null) { + if (rawUserAnswer != null) { answerText = rawUserAnswer.numeric } errorMessage.addOnPropertyChangedCallback(callback) @@ -97,7 +97,7 @@ class NumericInputViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - if(answerText.isNotEmpty()) { + if (answerText.isNotEmpty()) { val answerTextString = answerText.toString() numeric = answerTextString } 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 90ea607c462..2dae35e729f 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 @@ -53,7 +53,7 @@ class RatioExpressionInputInteractionViewModel private constructor( } } - if (rawUserAnswer!=null) { + if (rawUserAnswer != null) { answerText = rawUserAnswer.ratioInput } errorMessage.addOnPropertyChangedCallback(callback) 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 bfa23ab84ad..e7428b8d010 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 @@ -43,7 +43,7 @@ class TextInputViewModel private constructor( ) } } - if (rawUserAnswer!=null) { + if (rawUserAnswer != null) { answerText = rawUserAnswer.text } isAnswerAvailable.addOnPropertyChangedCallback(callback) @@ -80,7 +80,7 @@ class TextInputViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - text = answerText.toString() + text = answerText.toString() } }.build() 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 c5d248c19a0..18d6c8d8fd0 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 @@ -9,6 +9,7 @@ import org.oppia.android.app.fragment.FragmentComponentImpl 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -22,7 +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.RawUserAnswer /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : @@ -58,7 +58,9 @@ class QuestionPlayerFragment : rawUserAnswer = savedInstanceState.getProto("Answer", RawUserAnswer.getDefaultInstance()) } val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) - return questionPlayerFragmentPresenter.handleCreateView(inflater, container, rawUserAnswer, profileId) + return questionPlayerFragmentPresenter.handleCreateView( + inflater, container, rawUserAnswer, profileId + ) } 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 bf15c31766c..aa6e1ebd083 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 @@ -37,6 +37,8 @@ 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.RawUserAnswer +import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeStateRetention +import org.oppia.android.util.platformparameter.PlatformParameterValue /** The presenter for [QuestionPlayerFragment]. */ @FragmentScope @@ -47,6 +49,8 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private val questionAssessmentProgressController: QuestionAssessmentProgressController, private val oppiaLogger: OppiaLogger, @QuestionResourceBucketName private val resourceBucketName: String, + @EnableInteractionConfigChangeStateRetention + private val isConfigChangeStateRetentionEnabled: PlatformParameterValue, private val assemblerBuilderFactory: StatePlayerRecyclerViewAssembler.Builder.Factory, private val splitScreenManager: SplitScreenManager ) { @@ -186,7 +190,10 @@ class QuestionPlayerFragmentPresenter @Inject constructor( ) } - private fun processEphemeralQuestionResult(result: AsyncResult, rawUserAnswer: RawUserAnswer?) { + private fun processEphemeralQuestionResult( + result: AsyncResult, + rawUserAnswer: RawUserAnswer? + ) { when (result) { is AsyncResult.Failure -> { oppiaLogger.e( @@ -198,7 +205,10 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } } - private fun processEphemeralQuestion(ephemeralQuestion: EphemeralQuestion, rawUserAnswer: RawUserAnswer?) { + private fun processEphemeralQuestion( + ephemeralQuestion: EphemeralQuestion, + rawUserAnswer: RawUserAnswer? + ) { // TODO(#497): Update this to properly link to question assets. val skillId = ephemeralQuestion.question.linkedSkillIdsList.firstOrNull() ?: "" @@ -394,6 +404,10 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } fun handleOnSavedInstance(): RawUserAnswer { - return questionViewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) + return if (isConfigChangeStateRetentionEnabled.value) { + questionViewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) + } else { + RawUserAnswer.getDefaultInstance() + } } } 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 cb3e30b5e2e..374fed9099a 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,6 +4,7 @@ import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableField import androidx.databinding.ObservableList import org.oppia.android.R +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler @@ -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.RawUserAnswer /** [ObservableViewModel] for the question player. */ class QuestionPlayerViewModel @Inject constructor( 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 5ef1a4e5853..e5fa510edef 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 @@ -186,6 +186,11 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton +import kotlinx.android.synthetic.main.fraction_interaction_item.* +import org.oppia.android.app.customview.interaction.FractionInputInteractionView +import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationPortrait +import org.oppia.android.testing.platformparameter.TestPlatformParameterModule +import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeStateRetention /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -233,6 +238,7 @@ class StateFragmentTest { @Before fun setUp() { + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(false) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() @@ -1590,6 +1596,24 @@ class StateFragmentTest { } } + @Test + fun testStateFragment_fractionInput_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + clickContinueInteractionButton() + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(false) + //Entering text in Fraction Input Interaction + typeFractionText("1/2") + //Rotating device + rotateToLandscape() + it.onActivity { + val fractionInputInteraction = + it.findViewById(R.id.fraction_input_interaction_view) + assertThat(fractionInputInteraction.text.toString()).isEqualTo("1/2") + } + } + } + @Test fun testStateFragment_ratioInput_textViewHasTextInputType() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { scenario -> @@ -1626,9 +1650,9 @@ class StateFragmentTest { ) } - // TODO(#503): Add versions of the following multi-language & localization tests for questions. +// TODO(#503): Add versions of the following multi-language & localization tests for questions. - /* Multi-language & localization tests. */ +/* Multi-language & localization tests. */ @Test @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso. @@ -3582,8 +3606,8 @@ class StateFragmentTest { } } - // TODO(#3171): Implement image region selection tests for English/Arabic to demonstrate that - // answers submit normally & with no special behaviors. +// TODO(#3171): Implement image region selection tests for English/Arabic to demonstrate that +// answers submit normally & with no special behaviors. @Test fun testStateFragment_clickContinue_returnToState_doesNotHaveFeedbackBox() { @@ -4151,12 +4175,12 @@ class StateFragmentTest { return ApplicationProvider.getApplicationContext().isOnRobolectric() } - // TODO(#59): Remove these waits once we can ensure that the production executors are not depended on in tests. - // Sleeping is really bad practice in Espresso tests, and can lead to test flakiness. It shouldn't be necessary if we - // use a test executor service with a counting idle resource, but right now Gradle mixes dependencies such that both - // the test and production blocking executors are being used. The latter cannot be updated to notify Espresso of any - // active coroutines, so the test attempts to assert state before it's ready. This artificial delay in the Espresso - // thread helps to counter that. +// TODO(#59): Remove these waits once we can ensure that the production executors are not depended on in tests. +// Sleeping is really bad practice in Espresso tests, and can lead to test flakiness. It shouldn't be necessary if we +// use a test executor service with a counting idle resource, but right now Gradle mixes dependencies such that both +// the test and production blocking executors are being used. The latter cannot be updated to notify Espresso of any +// active coroutines, so the test attempts to assert state before it's ready. This artificial delay in the Espresso +// thread helps to counter that. /** * Perform action of waiting for a specific matcher to finish. Adapted from: * https://stackoverflow.com/a/22563297/3689782. From e8863a626d47890979e8d4e837c23eb4521f24c3 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 21 Oct 2022 23:31:04 +0530 Subject: [PATCH 06/76] Optimized code --- .../app/player/state/StateFragmentPresenter.kt | 4 ++-- .../player/state/StatePlayerRecyclerViewAssembler.kt | 2 +- .../QuestionPlayerFragmentPresenter.kt | 4 ++-- .../android/app/player/state/StateFragmentTest.kt | 12 +++++------- 4 files changed, 10 insertions(+), 12 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 ce4df8655e2..f2e73a56c22 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 @@ -49,10 +49,10 @@ import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.toLiveData 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.util.platformparameter.EnableInteractionConfigChangeStateRetention import org.oppia.android.util.platformparameter.PlatformParameterValue +import org.oppia.android.util.system.OppiaClock +import javax.inject.Inject const val STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_fragment_profile_id" 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 e8b01b94283..d256cfd5ac1 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 @@ -25,6 +25,7 @@ import org.oppia.android.app.model.EphemeralState.StateTypeCase import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.StringList import org.oppia.android.app.model.SubtitledHtml import org.oppia.android.app.model.UserAnswer @@ -94,7 +95,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.RawUserAnswer private typealias AudioUiManagerRetriever = () -> AudioUiManager? 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 aa6e1ebd083..fd72b9be75c 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 @@ -18,6 +18,7 @@ import org.oppia.android.app.model.EphemeralQuestion import org.oppia.android.app.model.EphemeralState import org.oppia.android.app.model.HelpIndex import org.oppia.android.app.model.ProfileId +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.State import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.player.state.ConfettiConfig.MINI_CONFETTI_BURST @@ -35,10 +36,9 @@ import org.oppia.android.util.data.AsyncResult 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.RawUserAnswer import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeStateRetention import org.oppia.android.util.platformparameter.PlatformParameterValue +import javax.inject.Inject /** The presenter for [QuestionPlayerFragment]. */ @FragmentScope 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 e5fa510edef..f8474e32956 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 @@ -48,6 +48,7 @@ import dagger.BindsInstance import dagger.Component import dagger.Module import dagger.Provides +import kotlinx.android.synthetic.main.fraction_interaction_item.* import kotlinx.coroutines.CoroutineDispatcher import org.hamcrest.BaseMatcher import org.hamcrest.CoreMatchers.allOf @@ -72,6 +73,7 @@ import org.oppia.android.app.application.ApplicationInjectorProvider import org.oppia.android.app.application.ApplicationModule import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule +import org.oppia.android.app.customview.interaction.FractionInputInteractionView import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.model.OppiaLanguage @@ -155,6 +157,7 @@ import org.oppia.android.testing.environment.TestEnvironmentConfig import org.oppia.android.testing.espresso.EditTextInputAction import org.oppia.android.testing.junit.InitializeDefaultLocaleRule import org.oppia.android.testing.lightweightcheckpointing.ExplorationCheckpointTestHelper +import org.oppia.android.testing.platformparameter.TestPlatformParameterModule import org.oppia.android.testing.profile.ProfileTestHelper import org.oppia.android.testing.robolectric.IsOnRobolectric import org.oppia.android.testing.robolectric.RobolectricModule @@ -186,11 +189,6 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton -import kotlinx.android.synthetic.main.fraction_interaction_item.* -import org.oppia.android.app.customview.interaction.FractionInputInteractionView -import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationPortrait -import org.oppia.android.testing.platformparameter.TestPlatformParameterModule -import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeStateRetention /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -1602,9 +1600,9 @@ class StateFragmentTest { startPlayingExploration() clickContinueInteractionButton() TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(false) - //Entering text in Fraction Input Interaction + // Entering text in Fraction Input Interaction typeFractionText("1/2") - //Rotating device + // Rotating device rotateToLandscape() it.onActivity { val fractionInputInteraction = From cf4f093058d4b6928d898bd6a0350b8d3eac06bd Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 24 Oct 2022 12:08:23 +0530 Subject: [PATCH 07/76] Kdoc added --- model/src/main/proto/exploration.proto | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 942715dd81a..0d21e9186c4 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -342,11 +342,15 @@ message RawUserAnswer { string number_with_units = 5; // A raw answer entered by user in RatioInputInteraction. string ratio_input = 6; - // A raw answer entered by user in MathInputInteraction with numeric expression. + // A raw answer entered by user in MathInputInteraction. string math_expression = 7; + // An indices selected in multiple choice selection. int32 multiple_choice_selection_index = 8; + // A selected index in SelectionInteraction. ItemSelectionRawAnswer item_selection = 9; + // A list of html content ids selected in DragAndDropInteraction. ListOfSetsOfTranslatableHtmlContentIds drag_and_drop = 10; + // A selected image region in ImageRegionSelectionInteraction. ClickOnImage image_region_selection = 11; } } From 1f0d530dd19dc38db70a50e30127c6f2fdb23834 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 24 Oct 2022 12:14:52 +0530 Subject: [PATCH 08/76] added platformparameter in bazel --- .../java/org/oppia/android/app/player/exploration/BUILD.bazel | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/BUILD.bazel index b63ad42bc76..8e1e93b4255 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/BUILD.bazel +++ b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/BUILD.bazel @@ -63,6 +63,7 @@ app_test( "//testing/src/main/java/org/oppia/android/testing/data:data_provider_test_monitor", "//testing/src/main/java/org/oppia/android/testing/espresso:edit_text_input_action", "//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule", + "//testing/src/main/java/org/oppia/android/testing/platformparameter:test_module", "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module", "//testing/src/main/java/org/oppia/android/testing/threading:coroutine_executor_service", "//testing/src/main/java/org/oppia/android/testing/threading:test_module", From 4de5907cf440377f7771c844fa2bbec8ad52f003 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 25 Oct 2022 01:06:49 +0530 Subject: [PATCH 09/76] Updated bazel --- .../java/org/oppia/android/app/player/exploration/BUILD.bazel | 1 - .../java/org/oppia/android/app/player/state/BUILD.bazel | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/BUILD.bazel index 8e1e93b4255..b63ad42bc76 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/BUILD.bazel +++ b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/BUILD.bazel @@ -63,7 +63,6 @@ app_test( "//testing/src/main/java/org/oppia/android/testing/data:data_provider_test_monitor", "//testing/src/main/java/org/oppia/android/testing/espresso:edit_text_input_action", "//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule", - "//testing/src/main/java/org/oppia/android/testing/platformparameter:test_module", "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module", "//testing/src/main/java/org/oppia/android/testing/threading:coroutine_executor_service", "//testing/src/main/java/org/oppia/android/testing/threading:test_module", diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/BUILD.bazel b/app/src/sharedTest/java/org/oppia/android/app/player/state/BUILD.bazel index 5f43c05d529..518f21c5fc6 100644 --- a/app/src/sharedTest/java/org/oppia/android/app/player/state/BUILD.bazel +++ b/app/src/sharedTest/java/org/oppia/android/app/player/state/BUILD.bazel @@ -27,6 +27,7 @@ app_test( "//testing/src/main/java/org/oppia/android/testing/data:data_provider_test_monitor", "//testing/src/main/java/org/oppia/android/testing/espresso:edit_text_input_action", "//testing/src/main/java/org/oppia/android/testing/junit:initialize_default_locale_rule", + "//testing/src/main/java/org/oppia/android/testing/platformparameter:test_module", "//testing/src/main/java/org/oppia/android/testing/robolectric:test_module", "//testing/src/main/java/org/oppia/android/testing/threading:coroutine_executor_service", "//testing/src/main/java/org/oppia/android/testing/threading:test_module", From d9a79bfda85feee436acb30ebab51f0e241acc30 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 25 Oct 2022 23:26:01 +0530 Subject: [PATCH 10/76] Improved data pipeline and nit changes --- .../ImageRegionSelectionInteractionView.kt | 14 +++ .../player/state/SelectionInteractionView.kt | 6 + .../android/app/player/state/StateFragment.kt | 12 +- .../player/state/StateFragmentPresenter.kt | 54 +++++---- .../state/StatePlayerRecyclerViewAssembler.kt | 31 ++++-- .../app/player/state/StateViewModel.kt | 13 +-- .../InteractionAnswerHandler.kt | 2 +- .../FractionInteractionViewModel.kt | 7 +- ...mageRegionSelectionInteractionViewModel.kt | 7 +- .../MathExpressionInteractionsViewModel.kt | 7 +- .../itemviewmodel/NumericInputViewModel.kt | 9 +- ...atioExpressionInputInteractionViewModel.kt | 7 +- .../SelectionInteractionViewModel.kt | 14 ++- .../state/itemviewmodel/TextInputViewModel.kt | 8 +- .../questionplayer/QuestionPlayerFragment.kt | 2 +- .../QuestionPlayerFragmentPresenter.kt | 23 ++-- .../questionplayer/QuestionPlayerViewModel.kt | 13 +-- ...mage_region_selection_interaction_item.xml | 1 + .../res/layout/selection_interaction_item.xml | 1 + .../app/player/state/StateFragmentTest.kt | 103 ++++++++++++++++-- model/src/main/proto/exploration.proto | 26 ++--- 21 files changed, 219 insertions(+), 141 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index d94b853f1ef..d9c43536229 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -2,12 +2,15 @@ package org.oppia.android.app.player.state import android.content.Context import android.util.AttributeSet +import android.util.Log import android.view.View import android.widget.FrameLayout import androidx.appcompat.widget.AppCompatImageView import androidx.core.view.forEachIndexed import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import org.oppia.android.R +import org.oppia.android.app.model.ClickOnImage import org.oppia.android.app.model.ImageWithRegions import org.oppia.android.app.shim.ViewBindingShim import org.oppia.android.app.utility.ClickableAreasImage @@ -51,6 +54,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( private lateinit var onRegionClicked: OnClickableAreaClickedListener private lateinit var imageUrl: String private lateinit var clickableAreas: List + private lateinit var clickedRegions: ClickOnImage /** * Sets the URL for the image & initiates loading it. This is intended to be called via @@ -66,6 +70,16 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( maybeInitializeClickableAreas() } + fun setClickedRegions(clickedRegions: ClickOnImage) { + this.clickedRegions = clickedRegions + val parentView = this.parent as FrameLayout + val newView = View(parentView.context) + newView.x = clickedRegions.clickPosition.x + newView.y = clickedRegions.clickPosition.y + newView.setBackgroundResource(R.drawable.selected_region_background) + maybeInitializeClickableAreas() + } + fun setClickableAreas(clickableAreas: List) { this.clickableAreas = clickableAreas // Resets the backgrounds for all regions if any have been loaded. This ensures the backgrounds diff --git a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt index 1265ed41808..7a93c8fb50f 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt @@ -39,6 +39,7 @@ class SelectionInteractionView @JvmOverloads constructor( private lateinit var entityId: String private lateinit var writtenTranslationContext: WrittenTranslationContext private lateinit var dataList: ObservableList + private lateinit var selectedItems: MutableList override fun onAttachedToWindow() { super.onAttachedToWindow() @@ -61,6 +62,11 @@ class SelectionInteractionView @JvmOverloads constructor( maybeInitializeAdapter() } + fun setSelectedItems(selectedItems: MutableList) { + this.selectedItems = selectedItems + maybeInitializeAdapter() + } + /** * Sets the [WrittenTranslationContext] used to translate strings in this view. * 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 df02137a5f9..9dc15b8015d 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 @@ -82,10 +82,9 @@ class StateFragment : val storyId = arguments!!.getStringFromBundle(STATE_FRAGMENT_STORY_ID_ARGUMENT_KEY)!! val explorationId = arguments!!.getStringFromBundle(STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY)!! - var rawUserAnswer: RawUserAnswer? = null - if (savedInstanceState != null) { - rawUserAnswer = savedInstanceState.getProto("Answer", RawUserAnswer.getDefaultInstance()) - } + val rawUserAnswer = savedInstanceState?.getProto( + STATE_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() + ) return stateFragmentPresenter.handleCreateView( inflater, container, @@ -139,7 +138,10 @@ class StateFragment : override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putProto("Answer", stateFragmentPresenter.handleOnSavedInstance()) + outState.putProto( + STATE_FRAGMENT_RAW_USER_ANSWER_KEY, + stateFragmentPresenter.getRawUserAnswer() + ) } fun revealSolution() = stateFragmentPresenter.revealSolution() 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 f2e73a56c22..a0650802b84 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 @@ -1,7 +1,6 @@ package org.oppia.android.app.player.state import android.content.Context -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -61,6 +60,7 @@ const val STATE_FRAGMENT_STORY_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_f const val STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_fragment_exploration_id" private const val TAG_AUDIO_FRAGMENT = "AUDIO_FRAGMENT" +const val STATE_FRAGMENT_RAW_USER_ANSWER_KEY = "StateFragmentPresenter.raw_user_answer" /** The presenter for [StateFragment]. */ @FragmentScope @@ -125,7 +125,7 @@ class StateFragmentPresenter @Inject constructor( /* attachToRoot= */ false ) recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, entityType, profileId), + assemblerBuilderFactory.create(resourceBucketName, entityType, profileId, rawUserAnswer), binding.congratulationsTextView, binding.congratulationsTextConfettiView, binding.fullScreenConfettiView @@ -165,7 +165,7 @@ class StateFragmentPresenter @Inject constructor( ) } - subscribeToCurrentState(rawUserAnswer) + subscribeToCurrentState() return binding.root } @@ -285,28 +285,27 @@ class StateFragmentPresenter @Inject constructor( return getAudioFragment() as? AudioUiManager } - private fun subscribeToCurrentState(rawUserAnswer: RawUserAnswer?) { + private fun subscribeToCurrentState() { ephemeralStateLiveData.observe( fragment, { result -> - processEphemeralStateResult(result, rawUserAnswer) + processEphemeralStateResult(result) } ) } private fun processEphemeralStateResult( result: AsyncResult, - rawUserAnswer: RawUserAnswer? ) { when (result) { is AsyncResult.Failure -> oppiaLogger.e("StateFragment", "Failed to retrieve ephemeral state", result.error) is AsyncResult.Pending -> {} // Display nothing until a valid result is available. - is AsyncResult.Success -> processEphemeralState(result.value, rawUserAnswer) + is AsyncResult.Success -> processEphemeralState(result.value) } } - private fun processEphemeralState(ephemeralState: EphemeralState, rawUserAnswer: RawUserAnswer?) { + private fun processEphemeralState(ephemeralState: EphemeralState) { explorationCheckpointState = ephemeralState.checkpointState val shouldSplit = splitScreenManager.shouldSplitScreen(ephemeralState.state.interaction.id) if (shouldSplit) { @@ -317,8 +316,6 @@ class StateFragmentPresenter @Inject constructor( viewModel.centerGuidelinePercentage.set(1f) } - Log.d("testAnswer", "Ephemeral State Called") - val isInNewState = ::currentStateName.isInitialized && currentStateName != ephemeralState.state.name @@ -330,7 +327,6 @@ class StateFragmentPresenter @Inject constructor( val dataPair = recyclerViewAssembler.compute( ephemeralState, explorationId, - rawUserAnswer, shouldSplit ) @@ -464,12 +460,10 @@ class StateFragmentPresenter @Inject constructor( /** Returns the checkpoint state for the current exploration. */ fun getExplorationCheckpointState() = explorationCheckpointState - fun handleOnSavedInstance(): RawUserAnswer { + fun getRawUserAnswer(): RawUserAnswer { return if (isConfigChangeStateRetentionEnabled.value) { viewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) - } else { - RawUserAnswer.getDefaultInstance() - } + } else RawUserAnswer.getDefaultInstance() } private fun markExplorationCompleted() { @@ -515,23 +509,25 @@ class StateFragmentPresenter @Inject constructor( private fun setHintOpenedAndUnRevealed(isHintUnrevealed: Boolean) { viewModel.setHintOpenedAndUnRevealedVisibility(isHintUnrevealed) - if (isHintUnrevealed) { - val hintBulbAnimation = AnimationUtils.loadAnimation( - context, - R.anim.hint_bulb_animation - ).also { it.interpolator = BounceUpAndDownInterpolator() } - - // The bulb should start bouncing every 30 seconds. Note that an initial delay is used for - // cases like configuration changes, or returning from a saved checkpoint. - lifecycleSafeTimerFactory.run { - activity.runPeriodically(delayMillis = 5_000, periodMillis = 30_000) { - return@runPeriodically viewModel.isHintOpenedAndUnRevealed.get()!!.also { playAnim -> - if (playAnim) binding.hintBulb.startAnimation(hintBulbAnimation) + if (!isConfigChangeStateRetentionEnabled.value) { + if (isHintUnrevealed) { + val hintBulbAnimation = AnimationUtils.loadAnimation( + context, + R.anim.hint_bulb_animation + ).also { it.interpolator = BounceUpAndDownInterpolator() } + + // The bulb should start bouncing every 30 seconds. Note that an initial delay is used for + // cases like configuration changes, or returning from a saved checkpoint. + lifecycleSafeTimerFactory.run { + activity.runPeriodically(delayMillis = 5_000, periodMillis = 30_000) { + return@runPeriodically viewModel.isHintOpenedAndUnRevealed.get()!!.also { playAnim -> + if (playAnim) binding.hintBulb.startAnimation(hintBulbAnimation) + } } } + } else { + binding.hintBulb.clearAnimation() } - } else { - binding.hintBulb.clearAnimation() } } 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 d256cfd5ac1..fab8104dc7e 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 @@ -145,7 +145,8 @@ class StatePlayerRecyclerViewAssembler private constructor( backgroundCoroutineDispatcher: CoroutineDispatcher, private val hasConversationView: Boolean, private val resourceHandler: AppLanguageResourceHandler, - private val translationController: TranslationController + private val translationController: TranslationController, + private val rawUserAnswer: RawUserAnswer?, ) : HtmlParser.CustomOppiaTagActionListener { /** * A list of view models corresponding to past view models that are hidden by default. These are @@ -196,7 +197,6 @@ class StatePlayerRecyclerViewAssembler private constructor( fun compute( ephemeralState: EphemeralState, gcsEntityId: String, - rawUserAnswer: RawUserAnswer?, isSplitView: Boolean ): Pair, List> { this.isSplitView.set(isSplitView) @@ -233,7 +233,6 @@ class StatePlayerRecyclerViewAssembler private constructor( interaction, hasPreviousState, gcsEntityId, - rawUserAnswer, ephemeralState.writtenTranslationContext ) } @@ -308,7 +307,6 @@ class StatePlayerRecyclerViewAssembler private constructor( interaction: Interaction, hasPreviousButton: Boolean, gcsEntityId: String, - rawUserAnswer: RawUserAnswer?, writtenTranslationContext: WrittenTranslationContext ) { val interactionViewModelFactory = interactionViewModelFactoryMap.getValue(interaction.id) @@ -894,7 +892,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 rawUserAnswer: RawUserAnswer? ) { private val adapterBuilder: BindableAdapter.MultiTypeBuilder) -> InteractionAnswerHandler? ): RawUserAnswer { - return getRawUserAnswerWithError( - retrieveAnswerHandler( - getAnswerItemList() - ) - ) ?: RawUserAnswer.getDefaultInstance() + return retrieveAnswerHandler(getAnswerItemList())?.getRawUserAnswer() + ?: RawUserAnswer.getDefaultInstance() } private fun getPendingAnswerWithoutError( @@ -77,12 +74,6 @@ class StateViewModel @Inject constructor() : ObservableViewModel() { } } - private fun getRawUserAnswerWithError( - answerHandler: InteractionAnswerHandler? - ): RawUserAnswer? { - return answerHandler?.getRawUserAnswer() - } - private fun getAnswerItemList(): List { return if (isSplitView.get() == true) { rightItemList 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 9a7d8377623..6fe295f510e 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 @@ -28,7 +28,7 @@ interface InteractionAnswerHandler { return null } - /** Return the last entered answer entered by user. */ + /** Return the last answer entered by user. */ fun getRawUserAnswer(): RawUserAnswer? { return null } 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 962b6b73b12..8b18652504f 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,7 +32,7 @@ class FractionInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.FRACTION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = "" + var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") val hintText: CharSequence = deriveHintText(interaction) @@ -48,9 +48,6 @@ class FractionInteractionViewModel private constructor( ) } } - if (rawUserAnswer != null) { - answerText = rawUserAnswer.fraction - } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) @@ -91,7 +88,7 @@ class FractionInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer? = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - fraction = answerText.toString() + textualAnswer = answerText.toString() } }.build() 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 32a5db5c080..1ce2a47e802 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 @@ -1,5 +1,6 @@ package org.oppia.android.app.player.state.itemviewmodel +import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R @@ -33,7 +34,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( ) : StateItemViewModel(ViewType.IMAGE_REGION_SELECTION_INTERACTION), InteractionAnswerHandler, OnClickableAreaClickedListener { - var answerText: CharSequence = "" + var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" val selectableRegions: List by lazy { val schemaObject = interaction.customizationArgsMap["imageAndRegions"] schemaObject?.customSchemaValue?.imageWithRegions?.labelRegionsList ?: listOf() @@ -44,6 +45,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( schemaObject?.customSchemaValue?.imageWithRegions?.imagePath ?: "" } + val clickedRegions = rawUserAnswer?.imageRegionSelection ?: ClickOnImage.getDefaultInstance() val isAnswerAvailable = ObservableField(false) init { @@ -87,8 +89,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - val answerTextString = answerText.toString() - imageRegionSelection = parseClickOnImage(answerTextString) + imageRegionSelection = parseClickOnImage(answerText.toString()) } }.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 29e122066d8..60a8e51802d 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 = "" + var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" /** * Defines whether an answer is currently available to parse. This is expected to be directly @@ -106,9 +106,6 @@ class MathExpressionInteractionsViewModel private constructor( } } - if (rawUserAnswer != null) { - answerText = rawUserAnswer.mathExpression - } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) @@ -149,7 +146,7 @@ class MathExpressionInteractionsViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - mathExpression = answerText.toString() + textualAnswer = answerText.toString() } }.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 d6034571715..05dba6b41c9 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,7 +26,7 @@ class NumericInputViewModel private constructor( private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler ) : StateItemViewModel(ViewType.NUMERIC_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = "" + var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" private var pendingAnswerError: String? = null val errorMessage = ObservableField("") var isAnswerAvailable = ObservableField(false) @@ -42,9 +42,7 @@ class NumericInputViewModel private constructor( ) } } - if (rawUserAnswer != null) { - answerText = rawUserAnswer.numeric - } + errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) @@ -98,8 +96,7 @@ class NumericInputViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - val answerTextString = answerText.toString() - numeric = answerTextString + textualAnswer = answerText.toString() } }.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 2dae35e729f..be79d4f2dd3 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,7 +33,7 @@ class RatioExpressionInputInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.RATIO_EXPRESSION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = "" + var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") @@ -53,9 +53,6 @@ class RatioExpressionInputInteractionViewModel private constructor( } } - if (rawUserAnswer != null) { - answerText = rawUserAnswer.ratioInput - } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) @@ -76,7 +73,7 @@ class RatioExpressionInputInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - ratioInput = answerText.toString() + textualAnswer = answerText.toString() } }.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 3aab5e3cd52..992664630dd 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.databinding.Observable import androidx.databinding.ObservableField import androidx.databinding.ObservableList @@ -17,6 +18,7 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiv import org.oppia.android.app.viewmodel.ObservableArrayList import org.oppia.android.domain.translation.TranslationController import javax.inject.Inject +import org.oppia.android.app.model.ItemSelectionRawAnswer /** Corresponds to the type of input that should be used for an item selection interaction view. */ enum class SelectionItemInputType { @@ -53,7 +55,11 @@ class SelectionInteractionViewModel private constructor( interaction.customizationArgsMap["maxAllowableSelectionCount"]?.signedInt ?: minAllowableSelectionCount } - private val selectedItems: MutableList = mutableListOf() + + val selectedItems: MutableList = mutableListOf() + val selectedAnswer: MutableList = + rawUserAnswer?.itemSelection?.selectedIndexesList ?: mutableListOf() + val choiceItems: ObservableList = computeChoiceItems(choiceSubtitledHtmls, hasConversationView, this) @@ -101,7 +107,11 @@ class SelectionInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - RawUserAnswer.getDefaultInstance() + if (selectedItems.size == 1) { + itemSelection = ItemSelectionRawAnswer.newBuilder().apply { + addSelectedIndexes(selectedItems.first()) + }.build() + } }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ 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 e7428b8d010..8e4146854f0 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 @@ -28,7 +28,7 @@ class TextInputViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController ) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = "" + var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" val hintText: CharSequence = deriveHintText(interaction) var isAnswerAvailable = ObservableField(false) @@ -43,9 +43,7 @@ class TextInputViewModel private constructor( ) } } - if (rawUserAnswer != null) { - answerText = rawUserAnswer.text - } + isAnswerAvailable.addOnPropertyChangedCallback(callback) } @@ -80,7 +78,7 @@ class TextInputViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - text = answerText.toString() + textualAnswer = answerText.toString() } }.build() 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 18d6c8d8fd0..a58bc4b65e8 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 @@ -92,7 +92,7 @@ class QuestionPlayerFragment : override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putProto("Answer", questionPlayerFragmentPresenter.handleOnSavedInstance()) + outState.putProto("Answer", questionPlayerFragmentPresenter.getRawUserAnswer()) } fun handleKeyboardAction() = questionPlayerFragmentPresenter.handleKeyboardAction() 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 fd72b9be75c..1a4732c7b96 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 @@ -83,9 +83,9 @@ class QuestionPlayerFragmentPresenter @Inject constructor( ) recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, "skill", profileId), + assemblerBuilderFactory.create(resourceBucketName, "skill", profileId, rawUserAnswer), binding.congratulationsTextView, - binding.congratulationsTextConfettiView + binding.congratulationsTextConfettiView, ) binding.apply { @@ -105,7 +105,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( helpIndex ) } - subscribeToCurrentQuestion(rawUserAnswer) + subscribeToCurrentQuestion() return binding.root } @@ -181,18 +181,17 @@ class QuestionPlayerFragmentPresenter @Inject constructor( showHintsAndSolutions(helpIndex, isCurrentStatePendingState) } - private fun subscribeToCurrentQuestion(rawUserAnswer: RawUserAnswer?) { + private fun subscribeToCurrentQuestion() { ephemeralQuestionLiveData.observe( fragment, Observer { - processEphemeralQuestionResult(it, rawUserAnswer) + processEphemeralQuestionResult(it) } ) } private fun processEphemeralQuestionResult( result: AsyncResult, - rawUserAnswer: RawUserAnswer? ) { when (result) { is AsyncResult.Failure -> { @@ -201,13 +200,12 @@ class QuestionPlayerFragmentPresenter @Inject constructor( ) } is AsyncResult.Pending -> {} // Display nothing until a valid result is available. - is AsyncResult.Success -> processEphemeralQuestion(result.value, rawUserAnswer) + is AsyncResult.Success -> processEphemeralQuestion(result.value) } } private fun processEphemeralQuestion( ephemeralQuestion: EphemeralQuestion, - rawUserAnswer: RawUserAnswer? ) { // TODO(#497): Update this to properly link to question assets. val skillId = ephemeralQuestion.question.linkedSkillIdsList.firstOrNull() ?: "" @@ -237,7 +235,6 @@ class QuestionPlayerFragmentPresenter @Inject constructor( val dataPair = recyclerViewAssembler.compute( ephemeralQuestion.ephemeralState, skillId, - rawUserAnswer, isSplitView ) @@ -337,7 +334,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private fun createRecyclerViewAssembler( builder: StatePlayerRecyclerViewAssembler.Builder, congratulationsTextView: TextView, - congratulationsTextConfettiView: KonfettiView + congratulationsTextConfettiView: KonfettiView, ): StatePlayerRecyclerViewAssembler { // TODO(#501): Add support early exit detection & message, which requires changes in the training progress // controller & possibly the ephemeral question data model. @@ -403,11 +400,9 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } } - fun handleOnSavedInstance(): RawUserAnswer { + fun getRawUserAnswer(): RawUserAnswer { return if (isConfigChangeStateRetentionEnabled.value) { questionViewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) - } else { - RawUserAnswer.getDefaultInstance() - } + } else RawUserAnswer.getDefaultInstance() } } 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 374fed9099a..162c34b89cc 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 @@ -106,17 +106,8 @@ class QuestionPlayerViewModel @Inject constructor( fun getRawUserAnswer( retrieveAnswerHandler: (List) -> InteractionAnswerHandler? ): RawUserAnswer { - return getRawUserAnswerWithError( - retrieveAnswerHandler( - getAnswerItemList() - ) - ) ?: RawUserAnswer.getDefaultInstance() - } - - private fun getRawUserAnswerWithError( - answerHandler: InteractionAnswerHandler? - ): RawUserAnswer? { - return answerHandler?.getRawUserAnswer() + return retrieveAnswerHandler(getAnswerItemList())?.getRawUserAnswer() + ?: RawUserAnswer.getDefaultInstance() } private fun getAnswerItemList(): List { diff --git a/app/src/main/res/layout/image_region_selection_interaction_item.xml b/app/src/main/res/layout/image_region_selection_interaction_item.xml index 375835975b7..c2ee0e9f6cb 100644 --- a/app/src/main/res/layout/image_region_selection_interaction_item.xml +++ b/app/src/main/res/layout/image_region_selection_interaction_item.xml @@ -52,6 +52,7 @@ app:entityId="@{viewModel.entityId}" app:imageUrl="@{viewModel.imagePath}" app:onRegionClicked="@{(region) -> viewModel.onClickableAreaTouched(region)}" + app:clickedRegions="@{viewModel.clickedRegions}" app:overlayView="@{interactionContainerFrameLayout}" /> 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 f8474e32956..99e0249eed9 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 @@ -48,7 +48,6 @@ import dagger.BindsInstance import dagger.Component import dagger.Module import dagger.Provides -import kotlinx.android.synthetic.main.fraction_interaction_item.* import kotlinx.coroutines.CoroutineDispatcher import org.hamcrest.BaseMatcher import org.hamcrest.CoreMatchers.allOf @@ -133,7 +132,6 @@ import org.oppia.android.domain.oppialogger.LoggingIdentifierModule import org.oppia.android.domain.oppialogger.analytics.ApplicationLifecycleModule import org.oppia.android.domain.oppialogger.logscheduler.MetricLogSchedulerModule import org.oppia.android.domain.oppialogger.loguploader.LogReportWorkerModule -import org.oppia.android.domain.platformparameter.PlatformParameterModule import org.oppia.android.domain.platformparameter.PlatformParameterSingletonModule import org.oppia.android.domain.question.QuestionModule import org.oppia.android.domain.topic.FRACTIONS_EXPLORATION_ID_1 @@ -189,6 +187,10 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton +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) @@ -236,7 +238,7 @@ class StateFragmentTest { @Before fun setUp() { - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(false) + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() @@ -1599,15 +1601,101 @@ class StateFragmentTest { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() clickContinueInteractionButton() - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(false) // Entering text in Fraction Input Interaction - typeFractionText("1/2") + typeFractionText("34k") // Rotating device rotateToLandscape() it.onActivity { val fractionInputInteraction = it.findViewById(R.id.fraction_input_interaction_view) - assertThat(fractionInputInteraction.text.toString()).isEqualTo("1/2") + assertThat(fractionInputInteraction.text.toString()).isEqualTo("34k") + } + onView(withId(R.id.fraction_input_error)).check(matches(isDisplayed())) + } + } + + @Test + fun testStateFragment_numericInput_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + // Entering text in Numeric Input + typeNumericInput("12a") + // Rotating device + rotateToLandscape() + it.onActivity { + val numericInputInteractionView = + it.findViewById(R.id.numeric_input_interaction_view) + assertThat(numericInputInteractionView.text.toString()).isEqualTo("12a") + } + onView(withId(R.id.number_input_error)).check(matches(isDisplayed())) + } + } + + @Test + fun testStateFragment_ratioInput_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + playThroughPrototypeState6() + // Entering text in Ratio Input Interaction + typeRatioExpression("3a:5") + // Rotating device + rotateToLandscape() + it.onActivity { + val ratioInputInteraction = + it.findViewById(R.id.ratio_input_interaction_view) + assertThat(ratioInputInteraction.text.toString()).isEqualTo("3a:5") + } + onView(withId(R.id.ratio_input_error)).check(matches(isDisplayed())) + } + } + + @Test + fun testStateFragment_textInput_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + playThroughPrototypeState6() + playThroughPrototypeState7() + // Enter text in Text Input Interaction + typeTextInput("finnish") + // Rotating device + rotateToLandscape() + it.onActivity { + val textInputInteraction = + it.findViewById(R.id.text_input_interaction_view) + assertThat(textInputInteraction.text.toString()).isEqualTo("finnish") + } + } + } + + @Test + fun testStateFragment_mathInteractions_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + startPlayingExploration() + // Enter text in Math Input Interaction + typeNumericExpression("1+2") + // Rotating device + rotateToLandscape() + it.onActivity { + val mathExpressionInteractionView = + it.findViewById( + R.id.math_expression_input_interaction_view + ) + assertThat(mathExpressionInteractionView.text.toString()).isEqualTo("1+2") } } } @@ -4321,7 +4409,8 @@ class StateFragmentTest { @Singleton @Component( modules = [ - TestModule::class, RobolectricModule::class, PlatformParameterModule::class, + TestPlatformParameterModule::class, + TestModule::class, RobolectricModule::class, TestDispatcherModule::class, ApplicationModule::class, LoggerModule::class, ContinueModule::class, FractionInputModule::class, ItemSelectionInputModule::class, MultipleChoiceInputModule::class, NumberWithUnitsRuleModule::class, diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 0d21e9186c4..c9ea241900e 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -327,31 +327,19 @@ message ItemSelectionRawAnswer { repeated int32 selected_indexes = 1; } -// Corresponds to a raw user answer that user has submitted. +// Corresponds to a last raw answer entered by user which is used to retain state on Config Change. message RawUserAnswer { oneof answer_input_type { - // Just indicates the presence of the type. - bool continue = 1; - // A raw answer entered by user in TextInputInteraction. - string text = 2; - // A raw answer entered by user in FactionInputInteraction. - string fraction = 3; - // A raw answer entered by user in NumericInputInteraction. - string numeric = 4; - // A raw answer entered by user in NumericInputInteraction with units. - string number_with_units = 5; - // A raw answer entered by user in RatioInputInteraction. - string ratio_input = 6; - // A raw answer entered by user in MathInputInteraction. - string math_expression = 7; + // A raw answer entered by user in Text based interactions. + string textual_answer = 1; // An indices selected in multiple choice selection. - int32 multiple_choice_selection_index = 8; + int32 multiple_choice_selection_index = 2; // A selected index in SelectionInteraction. - ItemSelectionRawAnswer item_selection = 9; + ItemSelectionRawAnswer item_selection = 3; // A list of html content ids selected in DragAndDropInteraction. - ListOfSetsOfTranslatableHtmlContentIds drag_and_drop = 10; + ListOfSetsOfTranslatableHtmlContentIds drag_and_drop = 4; // A selected image region in ImageRegionSelectionInteraction. - ClickOnImage image_region_selection = 11; + ClickOnImage image_region_selection = 5; } } From dbd3d559d717775041569c313a8338eb5cf91db9 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 25 Oct 2022 23:27:09 +0530 Subject: [PATCH 11/76] code optimized --- .../player/state/ImageRegionSelectionInteractionView.kt | 1 - .../ImageRegionSelectionInteractionViewModel.kt | 1 - .../state/itemviewmodel/SelectionInteractionViewModel.kt | 3 +-- .../oppia/android/app/player/state/StateFragmentTest.kt | 8 ++++---- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index d9c43536229..c0522f7a37e 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -2,7 +2,6 @@ package org.oppia.android.app.player.state import android.content.Context import android.util.AttributeSet -import android.util.Log import android.view.View import android.widget.FrameLayout import androidx.appcompat.widget.AppCompatImageView 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 1ce2a47e802..d94397ab980 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 @@ -1,6 +1,5 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R 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 992664630dd..880938a0eaf 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,11 +1,11 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.databinding.ObservableList import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject +import org.oppia.android.app.model.ItemSelectionRawAnswer import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds import org.oppia.android.app.model.SubtitledHtml @@ -18,7 +18,6 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiv import org.oppia.android.app.viewmodel.ObservableArrayList import org.oppia.android.domain.translation.TranslationController import javax.inject.Inject -import org.oppia.android.app.model.ItemSelectionRawAnswer /** Corresponds to the type of input that should be used for an item selection interaction view. */ enum class SelectionItemInputType { 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 99e0249eed9..066c0e219db 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 @@ -73,6 +73,10 @@ import org.oppia.android.app.application.ApplicationModule import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule 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 import org.oppia.android.app.devoptions.DeveloperOptionsModule import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule import org.oppia.android.app.model.OppiaLanguage @@ -187,10 +191,6 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton -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) From e151b9a325ee3088386ce52d69a02ec4febafdcb Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 25 Oct 2022 23:35:55 +0530 Subject: [PATCH 12/76] Updated imageRegionSelectionInteractionViewModel --- .../itemviewmodel/ImageRegionSelectionInteractionViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d94397ab980..ee7615769e1 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 @@ -33,7 +33,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( ) : StateItemViewModel(ViewType.IMAGE_REGION_SELECTION_INTERACTION), InteractionAnswerHandler, OnClickableAreaClickedListener { - var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" + var answerText: CharSequence = "" val selectableRegions: List by lazy { val schemaObject = interaction.customizationArgsMap["imageAndRegions"] schemaObject?.customSchemaValue?.imageWithRegions?.labelRegionsList ?: listOf() From c47ef4649f7595a46453fab7d3710ddb39da3711 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 27 Oct 2022 00:40:42 +0530 Subject: [PATCH 13/76] retained state in Selection Interaction --- .../state/ImageRegionSelectionInteractionView.kt | 13 ------------- .../app/player/state/SelectionInteractionView.kt | 6 ------ .../DragAndDropSortInteractionViewModel.kt | 10 +++++++--- .../ImageRegionSelectionInteractionViewModel.kt | 9 ++++----- .../itemviewmodel/SelectionInteractionViewModel.kt | 13 ++++++++++++- .../image_region_selection_interaction_item.xml | 1 - .../main/res/layout/selection_interaction_item.xml | 1 - .../platformparameter/PlatformParameterConstants.kt | 2 +- 8 files changed, 24 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index c0522f7a37e..d94b853f1ef 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -8,8 +8,6 @@ import androidx.appcompat.widget.AppCompatImageView import androidx.core.view.forEachIndexed import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager -import org.oppia.android.R -import org.oppia.android.app.model.ClickOnImage import org.oppia.android.app.model.ImageWithRegions import org.oppia.android.app.shim.ViewBindingShim import org.oppia.android.app.utility.ClickableAreasImage @@ -53,7 +51,6 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( private lateinit var onRegionClicked: OnClickableAreaClickedListener private lateinit var imageUrl: String private lateinit var clickableAreas: List - private lateinit var clickedRegions: ClickOnImage /** * Sets the URL for the image & initiates loading it. This is intended to be called via @@ -69,16 +66,6 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( maybeInitializeClickableAreas() } - fun setClickedRegions(clickedRegions: ClickOnImage) { - this.clickedRegions = clickedRegions - val parentView = this.parent as FrameLayout - val newView = View(parentView.context) - newView.x = clickedRegions.clickPosition.x - newView.y = clickedRegions.clickPosition.y - newView.setBackgroundResource(R.drawable.selected_region_background) - maybeInitializeClickableAreas() - } - fun setClickableAreas(clickableAreas: List) { this.clickableAreas = clickableAreas // Resets the backgrounds for all regions if any have been loaded. This ensures the backgrounds diff --git a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt index 7a93c8fb50f..1265ed41808 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt @@ -39,7 +39,6 @@ class SelectionInteractionView @JvmOverloads constructor( private lateinit var entityId: String private lateinit var writtenTranslationContext: WrittenTranslationContext private lateinit var dataList: ObservableList - private lateinit var selectedItems: MutableList override fun onAttachedToWindow() { super.onAttachedToWindow() @@ -62,11 +61,6 @@ class SelectionInteractionView @JvmOverloads constructor( maybeInitializeAdapter() } - fun setSelectedItems(selectedItems: MutableList) { - this.selectedItems = selectedItems - maybeInitializeAdapter() - } - /** * Sets the [WrittenTranslationContext] used to translate strings in this view. * 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 0c62edc5f19..5868e34c059 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 @@ -74,6 +74,7 @@ class DragAndDropSortInteractionViewModel private constructor( ) } } + isAnswerAvailable.addOnPropertyChangedCallback(callback) isAnswerAvailable.set(true) // For drag drop submit button will be enabled by default. } @@ -131,9 +132,12 @@ class DragAndDropSortInteractionViewModel private constructor( this@DragAndDropSortInteractionViewModel.writtenTranslationContext }.build() - override fun getRawUserAnswer(): RawUserAnswer { - return RawUserAnswer.getDefaultInstance() - } + override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { + val selectedLists = _choiceItems.map { it.htmlContent } + dragAndDrop = ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply { + addAllContentIdLists(selectedLists) + }.build() + }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ private fun convertItemsToAnswer(htmlItems: List): ListOfSetsOfHtmlStrings { 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 ee7615769e1..38b3b8a034b 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 @@ -1,5 +1,6 @@ package org.oppia.android.app.player.state.itemviewmodel +import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R @@ -43,8 +44,6 @@ class ImageRegionSelectionInteractionViewModel private constructor( val schemaObject = interaction.customizationArgsMap["imageAndRegions"] schemaObject?.customSchemaValue?.imageWithRegions?.imagePath ?: "" } - - val clickedRegions = rawUserAnswer?.imageRegionSelection ?: ClickOnImage.getDefaultInstance() val isAnswerAvailable = ObservableField(false) init { @@ -57,6 +56,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( ) } } + Log.d("testAnswer", rawUserAnswer?.imageRegionSelection?.clickPosition?.x.toString()) isAnswerAvailable.addOnPropertyChangedCallback(callback) } @@ -82,14 +82,13 @@ class ImageRegionSelectionInteractionViewModel private constructor( R.string.image_interaction_answer_text, answerTextString ) + Log.d("testAnswer", answerText.toString()) this.writtenTranslationContext = this@ImageRegionSelectionInteractionViewModel.writtenTranslationContext }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - if (answerText.isNotEmpty()) { - imageRegionSelection = parseClickOnImage(answerText.toString()) - } + imageRegionSelection = parseClickOnImage(answerText.toString()) }.build() private fun parseClickOnImage(answerTextString: String): ClickOnImage { 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 880938a0eaf..153e2da93e2 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 @@ -74,6 +74,13 @@ class SelectionInteractionViewModel private constructor( ) } } + if (selectedAnswer.size == 1) { + choiceItems[selectedAnswer[0]].handleItemClicked() + } else if (selectedAnswer.size > 1) { + selectedAnswer.forEach { index -> + choiceItems[index].handleItemClicked() + } + } isAnswerAvailable.addOnPropertyChangedCallback(callback) } @@ -106,7 +113,11 @@ class SelectionInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - if (selectedItems.size == 1) { + if (interactionId == "ItemSelectionInput") { + itemSelection = ItemSelectionRawAnswer.newBuilder().apply { + addAllSelectedIndexes(selectedItems) + }.build() + } else if (selectedItems.size == 1) { itemSelection = ItemSelectionRawAnswer.newBuilder().apply { addSelectedIndexes(selectedItems.first()) }.build() diff --git a/app/src/main/res/layout/image_region_selection_interaction_item.xml b/app/src/main/res/layout/image_region_selection_interaction_item.xml index c2ee0e9f6cb..375835975b7 100644 --- a/app/src/main/res/layout/image_region_selection_interaction_item.xml +++ b/app/src/main/res/layout/image_region_selection_interaction_item.xml @@ -52,7 +52,6 @@ app:entityId="@{viewModel.entityId}" app:imageUrl="@{viewModel.imagePath}" app:onRegionClicked="@{(region) -> viewModel.onClickableAreaTouched(region)}" - app:clickedRegions="@{viewModel.clickedRegions}" app:overlayView="@{interactionContainerFrameLayout}" /> diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index eb66db95f0a..ed7bd9e3667 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -215,4 +215,4 @@ const val ENABLE_SPOTLIGHT_UI_DEFAULT_VALUE = true annotation class EnableInteractionConfigChangeStateRetention /** Default value for feature flag corresponding to [EnableInteractionConfigChangeStateRetention]. */ -const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = false +const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true From aa6bd6d9b8bd3bc142ba28c201bbe08624e6a5d7 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 27 Oct 2022 22:20:44 +0530 Subject: [PATCH 14/76] Made rawUserAnwer to non-null and other nit changes --- .../android/app/player/state/StateFragment.kt | 4 +++- .../app/player/state/StateFragmentPresenter.kt | 12 ++++++------ .../state/StatePlayerRecyclerViewAssembler.kt | 12 +++++------- .../answerhandling/InteractionAnswerHandler.kt | 4 ++-- .../ContinueInteractionViewModel.kt | 6 +++--- .../DragAndDropSortInteractionViewModel.kt | 4 ++-- .../FractionInteractionViewModel.kt | 8 ++++---- ...ImageRegionSelectionInteractionViewModel.kt | 18 ++++++++++-------- .../MathExpressionInteractionsViewModel.kt | 6 +++--- .../itemviewmodel/NumericInputViewModel.kt | 6 +++--- ...RatioExpressionInputInteractionViewModel.kt | 6 +++--- .../SelectionInteractionViewModel.kt | 6 +++--- .../state/itemviewmodel/StateItemViewModel.kt | 2 +- .../state/itemviewmodel/TextInputViewModel.kt | 6 +++--- .../questionplayer/QuestionPlayerFragment.kt | 15 ++++++++++----- .../QuestionPlayerFragmentPresenter.kt | 2 +- .../app/player/state/StateFragmentTest.kt | 1 + .../PlatformParameterAlphaKenyaModule.kt | 11 +++++++++++ .../PlatformParameterModule.kt | 10 ++++++++++ model/src/main/proto/exploration.proto | 2 +- .../TestPlatformParameterModule.kt | 18 ++++++++++++++++++ .../PlatformParameterConstants.kt | 11 ++++++++++- 22 files changed, 113 insertions(+), 57 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 9dc15b8015d..f4c15429c83 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 @@ -25,6 +25,8 @@ import org.oppia.android.util.extensions.getStringFromBundle import org.oppia.android.util.extensions.putProto import javax.inject.Inject +private const val STATE_FRAGMENT_RAW_USER_ANSWER_KEY = "StateFragment.raw_user_answer" + /** Fragment that represents the current state of an exploration. */ class StateFragment : InjectableFragment(), @@ -84,7 +86,7 @@ class StateFragment : arguments!!.getStringFromBundle(STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY)!! val rawUserAnswer = savedInstanceState?.getProto( STATE_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() - ) + ) ?: RawUserAnswer.getDefaultInstance() return stateFragmentPresenter.handleCreateView( inflater, container, 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 a0650802b84..529ddbde607 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 @@ -52,6 +52,7 @@ import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeSta import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.system.OppiaClock import javax.inject.Inject +import org.oppia.android.util.platformparameter.EnableHintBulbAnimation const val STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_fragment_profile_id" @@ -60,7 +61,6 @@ const val STATE_FRAGMENT_STORY_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_f const val STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_fragment_exploration_id" private const val TAG_AUDIO_FRAGMENT = "AUDIO_FRAGMENT" -const val STATE_FRAGMENT_RAW_USER_ANSWER_KEY = "StateFragmentPresenter.raw_user_answer" /** The presenter for [StateFragment]. */ @FragmentScope @@ -77,6 +77,8 @@ class StateFragmentPresenter @Inject constructor( @DefaultResourceBucketName private val resourceBucketName: String, @EnableInteractionConfigChangeStateRetention private val isConfigChangeStateRetentionEnabled: PlatformParameterValue, + @EnableHintBulbAnimation + private val isHintBulbAnimationEnabled: PlatformParameterValue, private val assemblerBuilderFactory: StatePlayerRecyclerViewAssembler.Builder.Factory, private val splitScreenManager: SplitScreenManager, private val oppiaClock: OppiaClock @@ -111,7 +113,7 @@ class StateFragmentPresenter @Inject constructor( internalProfileId: Int, topicId: String, storyId: String, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, explorationId: String ): View? { profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() @@ -294,9 +296,7 @@ class StateFragmentPresenter @Inject constructor( ) } - private fun processEphemeralStateResult( - result: AsyncResult, - ) { + private fun processEphemeralStateResult(result: AsyncResult) { when (result) { is AsyncResult.Failure -> oppiaLogger.e("StateFragment", "Failed to retrieve ephemeral state", result.error) @@ -509,7 +509,7 @@ class StateFragmentPresenter @Inject constructor( private fun setHintOpenedAndUnRevealed(isHintUnrevealed: Boolean) { viewModel.setHintOpenedAndUnRevealedVisibility(isHintUnrevealed) - if (!isConfigChangeStateRetentionEnabled.value) { + if (isHintBulbAnimationEnabled.value) { if (isHintUnrevealed) { val hintBulbAnimation = AnimationUtils.loadAnimation( context, 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 fab8104dc7e..ba5d7da5960 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 @@ -146,7 +146,7 @@ class StatePlayerRecyclerViewAssembler private constructor( private val hasConversationView: Boolean, private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, - private val rawUserAnswer: RawUserAnswer?, + private val rawUserAnswer: RawUserAnswer, ) : HtmlParser.CustomOppiaTagActionListener { /** * A list of view models corresponding to past view models that are hidden by default. These are @@ -893,7 +893,7 @@ class StatePlayerRecyclerViewAssembler private constructor( private val translationController: TranslationController, private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, - private val rawUserAnswer: RawUserAnswer? + private val rawUserAnswer: RawUserAnswer ) { private val adapterBuilder: BindableAdapter.MultiTypeBuilder(false) var errorMessage = ObservableField("") val hintText: CharSequence = deriveHintText(interaction) @@ -86,7 +86,7 @@ class FractionInteractionViewModel private constructor( return pendingAnswerError } - override fun getRawUserAnswer(): RawUserAnswer? = RawUserAnswer.newBuilder().apply { + override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { textualAnswer = answerText.toString() } @@ -144,7 +144,7 @@ class FractionInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 38b3b8a034b..3333719d221 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,12 +20,13 @@ 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.Point2d /** [StateItemViewModel] for image region selection. */ class ImageRegionSelectionInteractionViewModel private constructor( val entityId: String, val hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, val isSplitView: Boolean, @@ -56,7 +57,6 @@ class ImageRegionSelectionInteractionViewModel private constructor( ) } } - Log.d("testAnswer", rawUserAnswer?.imageRegionSelection?.clickPosition?.x.toString()) isAnswerAvailable.addOnPropertyChangedCallback(callback) } @@ -82,7 +82,6 @@ class ImageRegionSelectionInteractionViewModel private constructor( R.string.image_interaction_answer_text, answerTextString ) - Log.d("testAnswer", answerText.toString()) this.writtenTranslationContext = this@ImageRegionSelectionInteractionViewModel.writtenTranslationContext }.build() @@ -93,10 +92,13 @@ class ImageRegionSelectionInteractionViewModel private constructor( private fun parseClickOnImage(answerTextString: String): ClickOnImage { val region = selectableRegions.find { it.label == answerTextString } - return ClickOnImage.newBuilder() - // The object supports multiple regions in an answer, but neither web nor Android supports this. - .addClickedRegions(region?.label ?: "") - .build() + return ClickOnImage.newBuilder().apply { + addClickedRegions(region?.label ?: "") + clickPosition = Point2d.newBuilder().apply { + x = region?.region?.area?.upperLeft?.x!! + y = region.region?.area?.lowerRight?.y!! + }.build() + }.build() } /** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */ @@ -106,7 +108,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 60a8e51802d..e26b2c3d7ff 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 @@ -60,7 +60,7 @@ import org.oppia.android.app.model.MathBinaryOperation.Operator as UnaryOperator class MathExpressionInteractionsViewModel private constructor( interaction: Interaction, val hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler, @@ -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 = rawUserAnswer?.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" /** * Defines whether an answer is currently available to parse. This is expected to be directly @@ -235,7 +235,7 @@ class MathExpressionInteractionsViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 05dba6b41c9..112928ea665 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 @@ -20,13 +20,13 @@ import javax.inject.Inject /** [StateItemViewModel] for the numeric input interaction. */ class NumericInputViewModel private constructor( val hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler ) : StateItemViewModel(ViewType.NUMERIC_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" private var pendingAnswerError: String? = null val errorMessage = ObservableField("") var isAnswerAvailable = ObservableField(false) @@ -107,7 +107,7 @@ class NumericInputViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 be79d4f2dd3..62f12344e3a 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 @@ -25,7 +25,7 @@ import javax.inject.Inject class RatioExpressionInputInteractionViewModel private constructor( interaction: Interaction, val hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, val isSplitView: Boolean, private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, private val writtenTranslationContext: WrittenTranslationContext, @@ -33,7 +33,7 @@ class RatioExpressionInputInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.RATIO_EXPRESSION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") @@ -145,7 +145,7 @@ class RatioExpressionInputInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 153e2da93e2..645b393bb07 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 @@ -29,7 +29,7 @@ enum class SelectionItemInputType { class SelectionInteractionViewModel private constructor( val entityId: String, val hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, @@ -57,7 +57,7 @@ class SelectionInteractionViewModel private constructor( val selectedItems: MutableList = mutableListOf() val selectedAnswer: MutableList = - rawUserAnswer?.itemSelection?.selectedIndexesList ?: mutableListOf() + rawUserAnswer.itemSelection.selectedIndexesList ?: mutableListOf() val choiceItems: ObservableList = computeChoiceItems(choiceSubtitledHtmls, hasConversationView, this) @@ -192,7 +192,7 @@ class SelectionInteractionViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 479ad2d8a04..034c6d6229e 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 @@ -48,7 +48,7 @@ abstract class StateItemViewModel(val viewType: ViewType) : ObservableViewModel( fun create( entityId: String, hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 8e4146854f0..8653c8be579 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 @@ -21,14 +21,14 @@ import javax.inject.Inject class TextInputViewModel private constructor( interaction: Interaction, val hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController ) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer?.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer : "" val hintText: CharSequence = deriveHintText(interaction) var isAnswerAvailable = ObservableField(false) @@ -111,7 +111,7 @@ class TextInputViewModel private constructor( override fun create( entityId: String, hasConversationView: Boolean, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, interaction: Interaction, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, 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 a58bc4b65e8..12e498ce675 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 @@ -24,6 +24,9 @@ import org.oppia.android.util.extensions.getProto import org.oppia.android.util.extensions.putProto import javax.inject.Inject +private const val QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY = + "QuestionPlayerFragment.raw_user_answer" + /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : InjectableFragment(), @@ -53,10 +56,9 @@ class QuestionPlayerFragment : val args = checkNotNull(arguments) { "Expected arguments to be passed to QuestionPlayerFragment" } - var rawUserAnswer: RawUserAnswer? = null - if (savedInstanceState != null) { - rawUserAnswer = savedInstanceState.getProto("Answer", RawUserAnswer.getDefaultInstance()) - } + val rawUserAnswer = savedInstanceState?.getProto( + QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() + ) ?: RawUserAnswer.getDefaultInstance() val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) return questionPlayerFragmentPresenter.handleCreateView( inflater, container, rawUserAnswer, profileId @@ -92,7 +94,10 @@ class QuestionPlayerFragment : override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putProto("Answer", questionPlayerFragmentPresenter.getRawUserAnswer()) + outState.putProto( + QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY, + questionPlayerFragmentPresenter.getRawUserAnswer() + ) } fun handleKeyboardAction() = questionPlayerFragmentPresenter.handleKeyboardAction() 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 1a4732c7b96..0ee10b8bb11 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 @@ -73,7 +73,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( fun handleCreateView( inflater: LayoutInflater, container: ViewGroup?, - rawUserAnswer: RawUserAnswer?, + rawUserAnswer: RawUserAnswer, profileId: ProfileId ): View? { binding = QuestionPlayerFragmentBinding.inflate( 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 066c0e219db..98e8391dd52 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 @@ -239,6 +239,7 @@ class StateFragmentTest { @Before fun setUp() { TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) + TestPlatformParameterModule.forceEnableHintBulbAnimation(false) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt index df66a77148d..324ee581ca6 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt @@ -2,6 +2,7 @@ package org.oppia.android.domain.platformparameter import dagger.Module import dagger.Provides +import okhttp3.internal.platform.Platform import org.oppia.android.util.platformparameter.AUTOMATIC_UPDATE_TOPIC_SETTING import org.oppia.android.util.platformparameter.AUTOMATIC_UPDATE_TOPIC_SETTING_VALUE import org.oppia.android.util.platformparameter.AutomaticUpdateTopicSetting @@ -10,12 +11,14 @@ import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VA import org.oppia.android.util.platformparameter.CacheLatexRendering import org.oppia.android.util.platformparameter.ENABLE_EDIT_ACCOUNTS_OPTIONS_UI_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_EXTRA_TOPIC_TABS_UI_DEFAULT_VALUE +import org.oppia.android.util.platformparameter.ENABLE_HINT_BULB_ANIMATION import org.oppia.android.util.platformparameter.ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION import org.oppia.android.util.platformparameter.ENABLE_LANGUAGE_SELECTION_UI_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_PERFORMANCE_METRICS_COLLECTION import org.oppia.android.util.platformparameter.ENABLE_PERFORMANCE_METRICS_COLLECTION_DEFAULT_VALUE import org.oppia.android.util.platformparameter.EnableEditAccountsOptionsUi import org.oppia.android.util.platformparameter.EnableExtraTopicTabsUi +import org.oppia.android.util.platformparameter.EnableHintBulbAnimation import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeStateRetention import org.oppia.android.util.platformparameter.EnableLanguageSelectionUi import org.oppia.android.util.platformparameter.EnablePerformanceMetricsCollection @@ -175,4 +178,12 @@ class PlatformParameterAlphaKenyaModule { ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION ) } + + @Provides + @EnableHintBulbAnimation + fun provideEnableHintBulbAnimation(): PlatformParameterValue { + return PlatformParameterValue.createDefaultParameter( + ENABLE_HINT_BULB_ANIMATION + ) + } } diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt index f49c42353f1..aa405c9e661 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterModule.kt @@ -10,6 +10,7 @@ import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VA import org.oppia.android.util.platformparameter.CacheLatexRendering import org.oppia.android.util.platformparameter.ENABLE_EDIT_ACCOUNTS_OPTIONS_UI_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_EXTRA_TOPIC_TABS_UI_DEFAULT_VALUE +import org.oppia.android.util.platformparameter.ENABLE_HINT_BULB_ANIMATION import org.oppia.android.util.platformparameter.ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION import org.oppia.android.util.platformparameter.ENABLE_LANGUAGE_SELECTION_UI_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_PERFORMANCE_METRICS_COLLECTION @@ -17,6 +18,7 @@ import org.oppia.android.util.platformparameter.ENABLE_PERFORMANCE_METRICS_COLLE import org.oppia.android.util.platformparameter.ENABLE_SPOTLIGHT_UI_DEFAULT_VALUE import org.oppia.android.util.platformparameter.EnableEditAccountsOptionsUi import org.oppia.android.util.platformparameter.EnableExtraTopicTabsUi +import org.oppia.android.util.platformparameter.EnableHintBulbAnimation import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeStateRetention import org.oppia.android.util.platformparameter.EnableLanguageSelectionUi import org.oppia.android.util.platformparameter.EnablePerformanceMetricsCollection @@ -181,4 +183,12 @@ class PlatformParameterModule { ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION ) } + + @Provides + @EnableHintBulbAnimation + fun provideEnableHintBulbAnimation(): PlatformParameterValue { + return PlatformParameterValue.createDefaultParameter( + ENABLE_HINT_BULB_ANIMATION + ) + } } diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index c9ea241900e..add7baa0f17 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -330,7 +330,7 @@ message ItemSelectionRawAnswer { // Corresponds to a last raw answer entered by user which is used to retain state on Config Change. message RawUserAnswer { oneof answer_input_type { - // A raw answer entered by user in Text based interactions. + // A raw answer entered by user in a text-based interaction. string textual_answer = 1; // An indices selected in multiple choice selection. int32 multiple_choice_selection_index = 2; diff --git a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt index dddedc84f38..e3f46757785 100644 --- a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt +++ b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt @@ -39,6 +39,9 @@ import org.oppia.android.util.platformparameter.SYNC_UP_WORKER_TIME_PERIOD_IN_HO import org.oppia.android.util.platformparameter.SplashScreenWelcomeMsg import org.oppia.android.util.platformparameter.SyncUpWorkerTimePeriodHours import javax.inject.Singleton +import org.oppia.android.app.model.PlatformParameter +import org.oppia.android.util.platformparameter.ENABLE_HINT_BULB_ANIMATION +import org.oppia.android.util.platformparameter.EnableHintBulbAnimation /* Fake Platform Parameter Module that provides individual Platform Parameters for testing. */ @Module @@ -198,6 +201,14 @@ class TestPlatformParameterModule { ) } + @Provides + @EnableHintBulbAnimation + fun provideEnableHintBulbAnimation(): PlatformParameterValue { + return PlatformParameterValue.createDefaultParameter( + enableHintBulbAnimation + ) + } + companion object { private var enableAutomaticUpdateTopicSettingUi = AUTOMATIC_UPDATE_TOPIC_SETTING_VALUE private var enableLanguageSelectionUi = ENABLE_LANGUAGE_SELECTION_UI_DEFAULT_VALUE @@ -206,6 +217,7 @@ class TestPlatformParameterModule { private var enableExtraTopicTabsUi = ENABLE_EXTRA_TOPIC_TABS_UI_DEFAULT_VALUE private var enableInteractionConfigChangeStateRetention = ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION + private var enableHintBulbAnimation = ENABLE_HINT_BULB_ANIMATION /** Enables forcing [EnableAutomaticUpdateTopicSettingUi] platform parameter flag from tests. */ @VisibleForTesting(otherwise = VisibleForTesting.NONE) @@ -242,5 +254,11 @@ class TestPlatformParameterModule { fun forceEnableInteractionConfigChangeStateRetention(value: Boolean) { enableInteractionConfigChangeStateRetention = value } + + /** Enables forcing [EnableHintBulbAnimation] platform parameter flag from tests. */ + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + fun forceEnableHintBulbAnimation(value: Boolean) { + enableInteractionConfigChangeStateRetention = value + } } } diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index ed7bd9e3667..82ed683d78f 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -208,7 +208,7 @@ annotation class EnableSpotlightUi const val ENABLE_SPOTLIGHT_UI_DEFAULT_VALUE = true /** - * Qualifier for the platform parameter that controls controls whether input interaction state is + * Qualifier for the platform parameter that controls whether input interaction state is * correctly retained across configuration changes. */ @Qualifier @@ -216,3 +216,12 @@ annotation class EnableInteractionConfigChangeStateRetention /** Default value for feature flag corresponding to [EnableInteractionConfigChangeStateRetention]. */ const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true + +/** + * Qualifier for the platform parameter that controls the animation for hint bulb animation + */ +@Qualifier +annotation class EnableHintBulbAnimation + +/** Default value for feature flag corresponding to [EnableHintBulbAnimation]. */ +const val ENABLE_HINT_BULB_ANIMATION = true \ No newline at end of file From b0706c04fe3068feac05be2645740d19743a39ab Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 27 Oct 2022 22:30:21 +0530 Subject: [PATCH 15/76] optimized code --- .../oppia/android/app/player/state/StateFragmentPresenter.kt | 2 +- .../ImageRegionSelectionInteractionViewModel.kt | 3 +-- .../app/player/state/itemviewmodel/TextInputViewModel.kt | 2 +- .../platformparameter/PlatformParameterAlphaKenyaModule.kt | 1 - .../testing/platformparameter/TestPlatformParameterModule.kt | 5 ++--- .../util/platformparameter/PlatformParameterConstants.kt | 2 +- 6 files changed, 6 insertions(+), 9 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 529ddbde607..f66ff02f622 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 @@ -48,11 +48,11 @@ import org.oppia.android.util.data.DataProvider import org.oppia.android.util.data.DataProviders.Companion.toLiveData import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.ExplorationHtmlParserEntityType +import org.oppia.android.util.platformparameter.EnableHintBulbAnimation import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeStateRetention import org.oppia.android.util.platformparameter.PlatformParameterValue import org.oppia.android.util.system.OppiaClock import javax.inject.Inject -import org.oppia.android.util.platformparameter.EnableHintBulbAnimation const val STATE_FRAGMENT_PROFILE_ID_ARGUMENT_KEY = "StateFragmentPresenter.state_fragment_profile_id" 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 3333719d221..df556891df8 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 @@ -1,6 +1,5 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R @@ -8,6 +7,7 @@ import org.oppia.android.app.model.ClickOnImage import org.oppia.android.app.model.ImageWithRegions import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject +import org.oppia.android.app.model.Point2d import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext @@ -20,7 +20,6 @@ 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.Point2d /** [StateItemViewModel] for image region selection. */ class ImageRegionSelectionInteractionViewModel private constructor( 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 8653c8be579..70bdeda7984 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 @@ -28,7 +28,7 @@ class TextInputViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController ) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer.textualAnswer : "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" val hintText: CharSequence = deriveHintText(interaction) var isAnswerAvailable = ObservableField(false) diff --git a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt index 324ee581ca6..54b0a18c35f 100644 --- a/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt +++ b/domain/src/main/java/org/oppia/android/domain/platformparameter/PlatformParameterAlphaKenyaModule.kt @@ -2,7 +2,6 @@ package org.oppia.android.domain.platformparameter import dagger.Module import dagger.Provides -import okhttp3.internal.platform.Platform import org.oppia.android.util.platformparameter.AUTOMATIC_UPDATE_TOPIC_SETTING import org.oppia.android.util.platformparameter.AUTOMATIC_UPDATE_TOPIC_SETTING_VALUE import org.oppia.android.util.platformparameter.AutomaticUpdateTopicSetting diff --git a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt index e3f46757785..500cdd18892 100644 --- a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt +++ b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt @@ -10,12 +10,14 @@ import org.oppia.android.util.platformparameter.CACHE_LATEX_RENDERING_DEFAULT_VA import org.oppia.android.util.platformparameter.CacheLatexRendering import org.oppia.android.util.platformparameter.ENABLE_EDIT_ACCOUNTS_OPTIONS_UI_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_EXTRA_TOPIC_TABS_UI_DEFAULT_VALUE +import org.oppia.android.util.platformparameter.ENABLE_HINT_BULB_ANIMATION import org.oppia.android.util.platformparameter.ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION import org.oppia.android.util.platformparameter.ENABLE_LANGUAGE_SELECTION_UI_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_PERFORMANCE_METRICS_COLLECTION import org.oppia.android.util.platformparameter.ENABLE_PERFORMANCE_METRICS_COLLECTION_DEFAULT_VALUE import org.oppia.android.util.platformparameter.EnableEditAccountsOptionsUi import org.oppia.android.util.platformparameter.EnableExtraTopicTabsUi +import org.oppia.android.util.platformparameter.EnableHintBulbAnimation import org.oppia.android.util.platformparameter.EnableInteractionConfigChangeStateRetention import org.oppia.android.util.platformparameter.EnableLanguageSelectionUi import org.oppia.android.util.platformparameter.EnablePerformanceMetricsCollection @@ -39,9 +41,6 @@ import org.oppia.android.util.platformparameter.SYNC_UP_WORKER_TIME_PERIOD_IN_HO import org.oppia.android.util.platformparameter.SplashScreenWelcomeMsg import org.oppia.android.util.platformparameter.SyncUpWorkerTimePeriodHours import javax.inject.Singleton -import org.oppia.android.app.model.PlatformParameter -import org.oppia.android.util.platformparameter.ENABLE_HINT_BULB_ANIMATION -import org.oppia.android.util.platformparameter.EnableHintBulbAnimation /* Fake Platform Parameter Module that provides individual Platform Parameters for testing. */ @Module diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index 82ed683d78f..ec5afdd9470 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -224,4 +224,4 @@ const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true annotation class EnableHintBulbAnimation /** Default value for feature flag corresponding to [EnableHintBulbAnimation]. */ -const val ENABLE_HINT_BULB_ANIMATION = true \ No newline at end of file +const val ENABLE_HINT_BULB_ANIMATION = true From a99e9fbcd89b542f549f9ca545a2b7e0b6a38e9e Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 27 Oct 2022 23:13:26 +0530 Subject: [PATCH 16/76] Fixed null value of rawUserAnswer --- .../android/app/testing/InputInteractionViewTestActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 8e06c97f75e..009296dd05d 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 @@ -36,6 +36,7 @@ import org.oppia.android.databinding.ActivityInputInteractionViewTestBinding import org.oppia.android.util.extensions.getProtoExtra import org.oppia.android.util.extensions.putProtoExtra import javax.inject.Inject +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.player.state.itemviewmodel.MathExpressionInteractionsViewModel.FactoryImpl.FactoryFactoryImpl as MathExpViewModelFactoryFactoryImpl /** @@ -158,7 +159,7 @@ class InputInteractionViewTestActivity : return create( entityId = "fake_entity_id", hasConversationView = false, - rawUserAnswer = null, + rawUserAnswer = RawUserAnswer.getDefaultInstance(), interaction = interaction, interactionAnswerReceiver = this@InputInteractionViewTestActivity, answerErrorReceiver = this@InputInteractionViewTestActivity, From 79442b77e1e44d7421e02605b5af15940f3948ca Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 27 Oct 2022 23:14:53 +0530 Subject: [PATCH 17/76] optimized imports --- .../android/app/testing/InputInteractionViewTestActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 009296dd05d..9bd66235064 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 @@ -18,6 +18,7 @@ import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathIn import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathInteractionType.NUMERIC_EXPRESSION import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathInteractionType.UNRECOGNIZED import org.oppia.android.app.model.Interaction +import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.SchemaObject import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext @@ -36,7 +37,6 @@ import org.oppia.android.databinding.ActivityInputInteractionViewTestBinding import org.oppia.android.util.extensions.getProtoExtra import org.oppia.android.util.extensions.putProtoExtra import javax.inject.Inject -import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.player.state.itemviewmodel.MathExpressionInteractionsViewModel.FactoryImpl.FactoryFactoryImpl as MathExpViewModelFactoryFactoryImpl /** From 84457fa8294f161514d28295a1c1f720a2e627cb Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 28 Oct 2022 18:17:57 +0530 Subject: [PATCH 18/76] DragAndDrop added --- .../DragAndDropSortInteractionViewModel.kt | 39 ++++++++++++++----- ...mageRegionSelectionInteractionViewModel.kt | 6 +++ model/src/main/proto/exploration.proto | 8 +++- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 01f91169943..d0de02ef280 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -1,5 +1,6 @@ package org.oppia.android.app.player.state.itemviewmodel +import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.recyclerview.widget.RecyclerView @@ -23,6 +24,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.DragAndDropRawAnswer /** [StateItemViewModel] for drag drop & sort choice list. */ class DragAndDropSortInteractionViewModel private constructor( @@ -43,11 +45,15 @@ class DragAndDropSortInteractionViewModel private constructor( interaction.customizationArgsMap["allowMultipleItemsInSamePosition"]?.boolValue ?: false } private val choiceSubtitledHtmls: List by lazy { - interaction.customizationArgsMap["choices"] - ?.schemaObjectList - ?.schemaObjectList - ?.map { schemaObject -> schemaObject.customSchemaValue.subtitledHtml } - ?: listOf() + if (rawUserAnswer.hasDragAndDrop()) { + rawUserAnswer.dragAndDrop.listOfSubtitledHtmlsList + } else { + interaction.customizationArgsMap["choices"] + ?.schemaObjectList + ?.schemaObjectList + ?.map { schemaObject -> schemaObject.customSchemaValue.subtitledHtml } + ?: listOf() + } } private val contentIdHtmlMap: Map = @@ -74,7 +80,11 @@ class DragAndDropSortInteractionViewModel private constructor( ) } } - + Log.d("testAnswer", "choiceSubtitledHtmls: $choiceSubtitledHtmls\n ") + if (rawUserAnswer != RawUserAnswer.getDefaultInstance()) + { + Log.d("testAnswer", rawUserAnswer.dragAndDrop.listOfSubtitledHtmlsOrBuilderList.toString()) + } isAnswerAvailable.addOnPropertyChangedCallback(callback) isAnswerAvailable.set(true) // For drag drop submit button will be enabled by default. } @@ -133,9 +143,20 @@ class DragAndDropSortInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - val selectedLists = _choiceItems.map { it.htmlContent } - dragAndDrop = ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply { - addAllContentIdLists(selectedLists) + val htmlContentIds = _choiceItems.map { it.htmlContent } + val htmlContent = _choiceItems.map { it.computeStringList() } + val listofSubtitledHtml = htmlContentIds.zip(htmlContent) + val SubtitleHtmlList = mutableListOf() + listofSubtitledHtml.forEach { + SubtitleHtmlList.add( + SubtitledHtml.newBuilder().apply { + contentId = it.first.contentIdsList[0].contentId + html = it.second.htmlList.first().toString() + }.build() + ) + } + dragAndDrop = DragAndDropRawAnswer.newBuilder().apply { + addAllListOfSubtitledHtmls(SubtitleHtmlList) }.build() }.build() 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 df556891df8..c61aa92b997 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 @@ -1,5 +1,6 @@ package org.oppia.android.app.player.state.itemviewmodel +import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R @@ -57,6 +58,11 @@ class ImageRegionSelectionInteractionViewModel private constructor( } } isAnswerAvailable.addOnPropertyChangedCallback(callback) + if(rawUserAnswer != RawUserAnswer.getDefaultInstance()) { + val imageRegionLabel = rawUserAnswer.imageRegionSelection.getClickedRegions(0) + val imageRegionPoint2d = rawUserAnswer.imageRegionSelection.clickPosition + Log.d("testAnswer", "$imageRegionLabel ${imageRegionPoint2d.x} ${imageRegionPoint2d.y}") + } } override fun onClickableAreaTouched(region: RegionClickedEvent) { diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index add7baa0f17..f90f0caceb3 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -336,13 +336,17 @@ message RawUserAnswer { int32 multiple_choice_selection_index = 2; // A selected index in SelectionInteraction. ItemSelectionRawAnswer item_selection = 3; - // A list of html content ids selected in DragAndDropInteraction. - ListOfSetsOfTranslatableHtmlContentIds drag_and_drop = 4; + // A selected html content and answer in DragAndDropInteraction. + DragAndDropRawAnswer drag_and_drop = 4; // A selected image region in ImageRegionSelectionInteraction. ClickOnImage image_region_selection = 5; } } +message DragAndDropRawAnswer { + repeated SubtitledHtml list_of_subtitled_htmls = 1; +} + message AnswerAndResponse { // A previous answer the learner submitted. UserAnswer user_answer = 1; From 4d63aacc9a869bcc2daddef0e6b92676b5a7f65c Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 28 Oct 2022 18:18:53 +0530 Subject: [PATCH 19/76] optimized code --- .../itemviewmodel/DragAndDropSortInteractionViewModel.kt | 5 ++--- .../ImageRegionSelectionInteractionViewModel.kt | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index d0de02ef280..7b6fedf370d 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -4,6 +4,7 @@ import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.recyclerview.widget.RecyclerView +import org.oppia.android.app.model.DragAndDropRawAnswer import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -24,7 +25,6 @@ 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.DragAndDropRawAnswer /** [StateItemViewModel] for drag drop & sort choice list. */ class DragAndDropSortInteractionViewModel private constructor( @@ -81,8 +81,7 @@ class DragAndDropSortInteractionViewModel private constructor( } } Log.d("testAnswer", "choiceSubtitledHtmls: $choiceSubtitledHtmls\n ") - if (rawUserAnswer != RawUserAnswer.getDefaultInstance()) - { + if (rawUserAnswer != RawUserAnswer.getDefaultInstance()) { Log.d("testAnswer", rawUserAnswer.dragAndDrop.listOfSubtitledHtmlsOrBuilderList.toString()) } isAnswerAvailable.addOnPropertyChangedCallback(callback) 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 c61aa92b997..72c51cba348 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 @@ -58,7 +58,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( } } isAnswerAvailable.addOnPropertyChangedCallback(callback) - if(rawUserAnswer != RawUserAnswer.getDefaultInstance()) { + if (rawUserAnswer != RawUserAnswer.getDefaultInstance()) { val imageRegionLabel = rawUserAnswer.imageRegionSelection.getClickedRegions(0) val imageRegionPoint2d = rawUserAnswer.imageRegionSelection.clickPosition Log.d("testAnswer", "$imageRegionLabel ${imageRegionPoint2d.x} ${imageRegionPoint2d.y}") From 313bb922627ff432d5e26ebd559b9a808fb15f9f Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 29 Oct 2022 01:12:50 +0530 Subject: [PATCH 20/76] ImageRegion made working --- .../ImageRegionSelectionInteractionView.kt | 12 ++++++ ...mageRegionSelectionInteractionViewModel.kt | 15 ++++---- .../app/utility/ClickableAreasImage.kt | 38 ++++++++++++++++++- ...mage_region_selection_interaction_item.xml | 1 + model/src/main/proto/exploration.proto | 2 +- 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index d94b853f1ef..327360d2826 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -1,7 +1,9 @@ package org.oppia.android.app.player.state import android.content.Context +import android.media.Image import android.util.AttributeSet +import android.util.Log import android.view.View import android.widget.FrameLayout import androidx.appcompat.widget.AppCompatImageView @@ -23,6 +25,7 @@ import org.oppia.android.util.parser.image.ImageDownloadUrlTemplate import org.oppia.android.util.parser.image.ImageLoader import org.oppia.android.util.parser.image.ImageViewTarget import javax.inject.Inject +import org.oppia.android.app.model.Point2d /** * A custom [AppCompatImageView] with a list of [ImageWithRegions.LabeledRegion]s to work with @@ -51,6 +54,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( private lateinit var onRegionClicked: OnClickableAreaClickedListener private lateinit var imageUrl: String private lateinit var clickableAreas: List + private lateinit var lastSelectedRegion: ImageWithRegions /** * Sets the URL for the image & initiates loading it. This is intended to be called via @@ -66,6 +70,11 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( maybeInitializeClickableAreas() } + fun setLastSelectedRegion(lastSelectedRegion: ImageWithRegions) { + this.lastSelectedRegion = lastSelectedRegion + maybeInitializeClickableAreas() + } + fun setClickableAreas(clickableAreas: List) { this.clickableAreas = clickableAreas // Resets the backgrounds for all regions if any have been loaded. This ensures the backgrounds @@ -120,6 +129,9 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( clickableAreas ) areasImage.addRegionViews() + if (lastSelectedRegion.labelRegionsCount > 0) { + areasImage.highlightBox(lastSelectedRegion.labelRegionsList[0]) + } performAttachment(areasImage) } } 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 72c51cba348..920ab309c28 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 @@ -46,6 +46,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( schemaObject?.customSchemaValue?.imageWithRegions?.imagePath ?: "" } val isAnswerAvailable = ObservableField(false) + val lastSelectedRegion = ObservableField(rawUserAnswer.imageRegionSelection) init { val callback: Observable.OnPropertyChangedCallback = @@ -58,11 +59,6 @@ class ImageRegionSelectionInteractionViewModel private constructor( } } isAnswerAvailable.addOnPropertyChangedCallback(callback) - if (rawUserAnswer != RawUserAnswer.getDefaultInstance()) { - val imageRegionLabel = rawUserAnswer.imageRegionSelection.getClickedRegions(0) - val imageRegionPoint2d = rawUserAnswer.imageRegionSelection.clickPosition - Log.d("testAnswer", "$imageRegionLabel ${imageRegionPoint2d.x} ${imageRegionPoint2d.y}") - } } override fun onClickableAreaTouched(region: RegionClickedEvent) { @@ -92,7 +88,10 @@ class ImageRegionSelectionInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - imageRegionSelection = parseClickOnImage(answerText.toString()) + val region = selectableRegions.find { it.label == answerText.toString() } + imageRegionSelection = ImageWithRegions.newBuilder().apply { + addLabelRegions(region) + }.build() }.build() private fun parseClickOnImage(answerTextString: String): ClickOnImage { @@ -100,8 +99,8 @@ class ImageRegionSelectionInteractionViewModel private constructor( return ClickOnImage.newBuilder().apply { addClickedRegions(region?.label ?: "") clickPosition = Point2d.newBuilder().apply { - x = region?.region?.area?.upperLeft?.x!! - y = region.region?.area?.lowerRight?.y!! + x = region?.region?.area?.upperLeft?.x ?: 0F + y = region?.region?.area?.lowerRight?.y ?: 0F }.build() }.build() } diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index 69d599fd2de..110ee4d5ec9 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -1,6 +1,7 @@ package org.oppia.android.app.utility import android.graphics.RectF +import android.util.Log import android.view.MotionEvent import android.view.View import android.widget.FrameLayout @@ -93,6 +94,7 @@ class ClickableAreasImage( getXCoordinate(clickableArea.region.area.lowerRight.x), getYCoordinate(clickableArea.region.area.lowerRight.y) ) + Log.d("TAGG", "addRegionViews: "+clickableArea.region.area.upperLeft) val layoutParams = FrameLayout.LayoutParams( imageRect.width().roundToInt(), imageRect.height().roundToInt() @@ -142,7 +144,9 @@ class ClickableAreasImage( } } - private fun showOrHideRegion(newView: View, clickableArea: ImageWithRegions.LabeledRegion) { + //Saturn + //You have selected planet Saturn + fun showOrHideRegion(newView: View, clickableArea: ImageWithRegions.LabeledRegion) { resetRegionSelectionViews() listener.onClickableAreaTouched( NamedRegionClickedEvent( @@ -152,4 +156,36 @@ class ClickableAreasImage( ) newView.setBackgroundResource(R.drawable.selected_region_background) } + + fun highlightBox(clickableArea: ImageWithRegions.LabeledRegion) { + // Remove all views other than the default region & selectable image. + parentView.children.filter { + it.id != imageView.id && it.id != defaultRegionView.id + }.forEach(parentView::removeView) + + val imageRect = RectF( + getXCoordinate(clickableArea.region.area.upperLeft.x), + getYCoordinate(clickableArea.region.area.upperLeft.y), + getXCoordinate(clickableArea.region.area.lowerRight.x), + getYCoordinate(clickableArea.region.area.lowerRight.y) + ) + val layoutParams = FrameLayout.LayoutParams( + imageRect.width().roundToInt(), + imageRect.height().roundToInt() + ) + val newView = View(parentView.context) + + ViewCompat.setLayoutDirection(parentView, ViewCompat.LAYOUT_DIRECTION_LTR) + newView.layoutParams = layoutParams + newView.x = imageRect.left + newView.y = imageRect.top + newView.isClickable = true + newView.isFocusable = true + newView.isFocusableInTouchMode = true + newView.tag = clickableArea.label + newView.contentDescription = clickableArea.contentDescription + parentView.addView(newView) + + showOrHideRegion(newView, clickableArea) + } } diff --git a/app/src/main/res/layout/image_region_selection_interaction_item.xml b/app/src/main/res/layout/image_region_selection_interaction_item.xml index 375835975b7..eb44937779e 100644 --- a/app/src/main/res/layout/image_region_selection_interaction_item.xml +++ b/app/src/main/res/layout/image_region_selection_interaction_item.xml @@ -50,6 +50,7 @@ android:adjustViewBounds="true" app:clickableAreas="@{viewModel.selectableRegions}" app:entityId="@{viewModel.entityId}" + app:lastSelectedRegion="@{viewModel.lastSelectedRegion}" app:imageUrl="@{viewModel.imagePath}" app:onRegionClicked="@{(region) -> viewModel.onClickableAreaTouched(region)}" app:overlayView="@{interactionContainerFrameLayout}" /> diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index f90f0caceb3..448b27e6a97 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -339,7 +339,7 @@ message RawUserAnswer { // A selected html content and answer in DragAndDropInteraction. DragAndDropRawAnswer drag_and_drop = 4; // A selected image region in ImageRegionSelectionInteraction. - ClickOnImage image_region_selection = 5; + ImageWithRegions image_region_selection = 5; } } From 59993af9eae63a27eb5e2431687a6b2c188e3419 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 29 Oct 2022 01:39:53 +0530 Subject: [PATCH 21/76] Optimized code ImageRegionSelection --- .../player/state/ImageRegionSelectionInteractionView.kt | 8 ++++---- .../ImageRegionSelectionInteractionViewModel.kt | 8 +++----- .../org/oppia/android/app/utility/ClickableAreasImage.kt | 5 ----- .../layout/image_region_selection_interaction_item.xml | 2 +- model/src/main/proto/exploration.proto | 2 +- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index 327360d2826..081bd23024b 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -54,7 +54,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( private lateinit var onRegionClicked: OnClickableAreaClickedListener private lateinit var imageUrl: String private lateinit var clickableAreas: List - private lateinit var lastSelectedRegion: ImageWithRegions + private lateinit var lastSelectedRegion: ImageWithRegions.LabeledRegion /** * Sets the URL for the image & initiates loading it. This is intended to be called via @@ -70,7 +70,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( maybeInitializeClickableAreas() } - fun setLastSelectedRegion(lastSelectedRegion: ImageWithRegions) { + fun setLastSelectedRegion(lastSelectedRegion: ImageWithRegions.LabeledRegion) { this.lastSelectedRegion = lastSelectedRegion maybeInitializeClickableAreas() } @@ -129,8 +129,8 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( clickableAreas ) areasImage.addRegionViews() - if (lastSelectedRegion.labelRegionsCount > 0) { - areasImage.highlightBox(lastSelectedRegion.labelRegionsList[0]) + if (lastSelectedRegion.hasRegion()) { + areasImage.highlightBox(lastSelectedRegion) } performAttachment(areasImage) } 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 920ab309c28..47b6c6dbfab 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 @@ -46,7 +46,8 @@ class ImageRegionSelectionInteractionViewModel private constructor( schemaObject?.customSchemaValue?.imageWithRegions?.imagePath ?: "" } val isAnswerAvailable = ObservableField(false) - val lastSelectedRegion = ObservableField(rawUserAnswer.imageRegionSelection) + val lastSelectedRegion = + ObservableField(rawUserAnswer.imageRegionSelection) init { val callback: Observable.OnPropertyChangedCallback = @@ -88,10 +89,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - val region = selectableRegions.find { it.label == answerText.toString() } - imageRegionSelection = ImageWithRegions.newBuilder().apply { - addLabelRegions(region) - }.build() + imageRegionSelection = selectableRegions.find { it.label == answerText.toString() } }.build() private fun parseClickOnImage(answerTextString: String): ClickOnImage { diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index 110ee4d5ec9..19e9c27162e 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -158,11 +158,6 @@ class ClickableAreasImage( } fun highlightBox(clickableArea: ImageWithRegions.LabeledRegion) { - // Remove all views other than the default region & selectable image. - parentView.children.filter { - it.id != imageView.id && it.id != defaultRegionView.id - }.forEach(parentView::removeView) - val imageRect = RectF( getXCoordinate(clickableArea.region.area.upperLeft.x), getYCoordinate(clickableArea.region.area.upperLeft.y), diff --git a/app/src/main/res/layout/image_region_selection_interaction_item.xml b/app/src/main/res/layout/image_region_selection_interaction_item.xml index eb44937779e..0f1162aa4da 100644 --- a/app/src/main/res/layout/image_region_selection_interaction_item.xml +++ b/app/src/main/res/layout/image_region_selection_interaction_item.xml @@ -50,8 +50,8 @@ android:adjustViewBounds="true" app:clickableAreas="@{viewModel.selectableRegions}" app:entityId="@{viewModel.entityId}" - app:lastSelectedRegion="@{viewModel.lastSelectedRegion}" app:imageUrl="@{viewModel.imagePath}" + app:lastSelectedRegion="@{viewModel.lastSelectedRegion}" app:onRegionClicked="@{(region) -> viewModel.onClickableAreaTouched(region)}" app:overlayView="@{interactionContainerFrameLayout}" /> diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 448b27e6a97..07352ba5b66 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -339,7 +339,7 @@ message RawUserAnswer { // A selected html content and answer in DragAndDropInteraction. DragAndDropRawAnswer drag_and_drop = 4; // A selected image region in ImageRegionSelectionInteraction. - ImageWithRegions image_region_selection = 5; + ImageWithRegions.LabeledRegion image_region_selection = 5; } } From 5a367881977704173ff8c45724410058c292f09d Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 29 Oct 2022 01:42:18 +0530 Subject: [PATCH 22/76] Optimized code --- .../app/player/state/ImageRegionSelectionInteractionView.kt | 3 --- .../ImageRegionSelectionInteractionViewModel.kt | 1 - .../org/oppia/android/app/utility/ClickableAreasImage.kt | 6 +++--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index 081bd23024b..03ede90c4a0 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -1,9 +1,7 @@ package org.oppia.android.app.player.state import android.content.Context -import android.media.Image import android.util.AttributeSet -import android.util.Log import android.view.View import android.widget.FrameLayout import androidx.appcompat.widget.AppCompatImageView @@ -25,7 +23,6 @@ import org.oppia.android.util.parser.image.ImageDownloadUrlTemplate import org.oppia.android.util.parser.image.ImageLoader import org.oppia.android.util.parser.image.ImageViewTarget import javax.inject.Inject -import org.oppia.android.app.model.Point2d /** * A custom [AppCompatImageView] with a list of [ImageWithRegions.LabeledRegion]s to work with 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 47b6c6dbfab..80decef7439 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 @@ -1,6 +1,5 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index 19e9c27162e..c461d62a484 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -94,7 +94,7 @@ class ClickableAreasImage( getXCoordinate(clickableArea.region.area.lowerRight.x), getYCoordinate(clickableArea.region.area.lowerRight.y) ) - Log.d("TAGG", "addRegionViews: "+clickableArea.region.area.upperLeft) + Log.d("TAGG", "addRegionViews: " + clickableArea.region.area.upperLeft) val layoutParams = FrameLayout.LayoutParams( imageRect.width().roundToInt(), imageRect.height().roundToInt() @@ -144,8 +144,8 @@ class ClickableAreasImage( } } - //Saturn - //You have selected planet Saturn + // Saturn + // You have selected planet Saturn fun showOrHideRegion(newView: View, clickableArea: ImageWithRegions.LabeledRegion) { resetRegionSelectionViews() listener.onClickableAreaTouched( From c8587f92cf0a5776c89348214d76977004fe1f25 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sun, 30 Oct 2022 00:49:36 +0530 Subject: [PATCH 23/76] Optimized code --- .../ContinueInteractionViewModel.kt | 4 --- .../DragAndDropSortInteractionViewModel.kt | 28 +++++++++++++++++-- .../SelectionInteractionViewModel.kt | 23 ++++++++------- .../QuestionPlayerFragmentPresenter.kt | 10 ++----- model/src/main/proto/exploration.proto | 15 +++++----- 5 files changed, 48 insertions(+), 32 deletions(-) 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 5535962195e..f43189bbc7c 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 @@ -43,10 +43,6 @@ class ContinueInteractionViewModel private constructor( this.writtenTranslationContext = this@ContinueInteractionViewModel.writtenTranslationContext }.build() - override fun getRawUserAnswer(): RawUserAnswer { - return RawUserAnswer.getDefaultInstance() - } - fun handleButtonClicked() { interactionAnswerReceiver.onAnswerReadyForSubmission(getPendingAnswer()) } 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 7b6fedf370d..9be34d05337 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 @@ -144,16 +144,38 @@ class DragAndDropSortInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { val htmlContentIds = _choiceItems.map { it.htmlContent } val htmlContent = _choiceItems.map { it.computeStringList() } - val listofSubtitledHtml = htmlContentIds.zip(htmlContent) + val listofHtmlContentIds = mutableListOf() + val listofHtmlContent = mutableListOf() + htmlContentIds.forEach { + if (it.contentIdsCount > 1) { + it.contentIdsList.forEach { + listofHtmlContentIds.add(it.contentId) + } + } else { + listofHtmlContentIds.add(it.contentIdsList[0].contentId) + } + } + htmlContent.forEach { + if (it.htmlCount > 1) { + it.htmlList.forEach { + listofHtmlContent.add(it) + } + } else { + listofHtmlContent.add(it.htmlList.first().toString()) + } + } + val listofSubtitledHtml = listofHtmlContentIds.zip(listofHtmlContent) + Log.d("TAGG", "getRawUserAnswer: listOfSubtitledHtml $_choiceItems") val SubtitleHtmlList = mutableListOf() listofSubtitledHtml.forEach { SubtitleHtmlList.add( SubtitledHtml.newBuilder().apply { - contentId = it.first.contentIdsList[0].contentId - html = it.second.htmlList.first().toString() + contentId = it.first + html = it.second }.build() ) } + Log.d("TAGG", "getRawUserAnswer: SubtitleHtmlList " + SubtitleHtmlList.toString()) dragAndDrop = DragAndDropRawAnswer.newBuilder().apply { addAllListOfSubtitledHtmls(SubtitleHtmlList) }.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 645b393bb07..01accd57f34 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 @@ -56,7 +56,7 @@ class SelectionInteractionViewModel private constructor( } val selectedItems: MutableList = mutableListOf() - val selectedAnswer: MutableList = + private val selectedAnswer: MutableList = rawUserAnswer.itemSelection.selectedIndexesList ?: mutableListOf() val choiceItems: ObservableList = @@ -75,10 +75,15 @@ class SelectionInteractionViewModel private constructor( } } if (selectedAnswer.size == 1) { - choiceItems[selectedAnswer[0]].handleItemClicked() + val selectedIndex = selectedAnswer[0] + val isAnswerUpdated = + updateSelection(selectedIndex, choiceItems[selectedIndex].isAnswerSelected.get()) + choiceItems[selectedIndex].isAnswerSelected.set(isAnswerUpdated) } else if (selectedAnswer.size > 1) { selectedAnswer.forEach { index -> - choiceItems[index].handleItemClicked() + val isAnswerUpdated = + updateSelection(index, choiceItems[index].isAnswerSelected.get()) + choiceItems[index].isAnswerSelected.set(isAnswerUpdated) } } isAnswerAvailable.addOnPropertyChangedCallback(callback) @@ -113,15 +118,13 @@ class SelectionInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - if (interactionId == "ItemSelectionInput") { - itemSelection = ItemSelectionRawAnswer.newBuilder().apply { + itemSelection = ItemSelectionRawAnswer.newBuilder().apply { + if (interactionId == "ItemSelectionInput") { addAllSelectedIndexes(selectedItems) - }.build() - } else if (selectedItems.size == 1) { - itemSelection = ItemSelectionRawAnswer.newBuilder().apply { + } else if (selectedItems.size == 1) { addSelectedIndexes(selectedItems.first()) - }.build() - } + } + }.build() }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ 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 0ee10b8bb11..c1c44411c75 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 @@ -190,9 +190,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( ) } - private fun processEphemeralQuestionResult( - result: AsyncResult, - ) { + private fun processEphemeralQuestionResult(result: AsyncResult) { when (result) { is AsyncResult.Failure -> { oppiaLogger.e( @@ -204,9 +202,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } } - private fun processEphemeralQuestion( - ephemeralQuestion: EphemeralQuestion, - ) { + private fun processEphemeralQuestion(ephemeralQuestion: EphemeralQuestion) { // TODO(#497): Update this to properly link to question assets. val skillId = ephemeralQuestion.question.linkedSkillIdsList.firstOrNull() ?: "" @@ -334,7 +330,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private fun createRecyclerViewAssembler( builder: StatePlayerRecyclerViewAssembler.Builder, congratulationsTextView: TextView, - congratulationsTextConfettiView: KonfettiView, + congratulationsTextConfettiView: KonfettiView ): StatePlayerRecyclerViewAssembler { // TODO(#501): Add support early exit detection & message, which requires changes in the training progress // controller & possibly the ephemeral question data model. diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 07352ba5b66..cdf05a907dd 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -322,23 +322,22 @@ message UserAnswer { WrittenTranslationContext written_translation_context = 6; } -// Corresponds to a raw item selection answer that user has submitted. +// Corresponds to a raw item selection answer that user has selected. message ItemSelectionRawAnswer { repeated int32 selected_indexes = 1; } -// Corresponds to a last raw answer entered by user which is used to retain state on Config Change. +// Corresponds to a raw representation of the current answer entered by the user which is used to +// retain state on configuration changes. message RawUserAnswer { oneof answer_input_type { - // A raw answer entered by user in a text-based interaction. + // A raw answer entered by user in a text-based interactions. string textual_answer = 1; - // An indices selected in multiple choice selection. - int32 multiple_choice_selection_index = 2; - // A selected index in SelectionInteraction. + // A user's selection for item selection and multiple choice interactions. ItemSelectionRawAnswer item_selection = 3; - // A selected html content and answer in DragAndDropInteraction. + // A user's selection for drag and drop interactions. DragAndDropRawAnswer drag_and_drop = 4; - // A selected image region in ImageRegionSelectionInteraction. + // A user's selection for image region selection interaction. ImageWithRegions.LabeledRegion image_region_selection = 5; } } From 959877cedbf5dc175fdcfcf24e09b9e193850b5b Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 31 Oct 2022 00:14:13 +0530 Subject: [PATCH 24/76] Added tests for selection and drag and drop interaction --- .../app/player/state/StateFragmentTest.kt | 96 +++++++++++++++++++ .../PlatformParameterConstants.kt | 2 +- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt index 98e8391dd52..42b927a18a2 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 @@ -28,6 +28,7 @@ import androidx.test.espresso.contrib.RecyclerViewActions.scrollToHolder import androidx.test.espresso.intent.Intents import androidx.test.espresso.matcher.RootMatchers.isDialog 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 @@ -1599,6 +1600,7 @@ class StateFragmentTest { @Test fun testStateFragment_fractionInput_retainStateOnConfigurationChange() { + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() clickContinueInteractionButton() @@ -1701,6 +1703,100 @@ class StateFragmentTest { } } + + @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + fun testStateFragment_selectionInteraction_ratioButton_retainStateOnConfigurationChange() { + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + 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())) + } + } + + @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + fun testStateFragment_selectionInteraction_multipleSelection_retainStateOnConfigurationChange() { + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + // Select answer. + 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())) + } + } + + @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + fun testStateFragment_dragAndDrop_retainStateOnConfigurationChange() { + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + playThroughPrototypeState1() + playThroughPrototypeState2() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + playThroughPrototypeState6() + playThroughPrototypeState7() + playThroughPrototypeState8() + + // Drag and drop interaction without grouping. + dragAndDropItem(fromPosition = 0, toPosition = 3) + // Rotating device. + rotateToLandscape() + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 3, + targetViewId = R.id.drag_drop_content_text_view + ) + ).check(matches(withText(containsString("0.35")))) + } + } + @Test fun testStateFragment_ratioInput_textViewHasTextInputType() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { scenario -> diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index ec5afdd9470..7810bfcbe38 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -224,4 +224,4 @@ const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true annotation class EnableHintBulbAnimation /** Default value for feature flag corresponding to [EnableHintBulbAnimation]. */ -const val ENABLE_HINT_BULB_ANIMATION = true +const val ENABLE_HINT_BULB_ANIMATION = false From 9d3d27c264205e28db1e8d359ecbcd23cb37e2e1 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 31 Oct 2022 00:15:05 +0530 Subject: [PATCH 25/76] optimized code --- .../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 42b927a18a2..bd2d4634a90 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 @@ -1703,7 +1703,6 @@ class StateFragmentTest { } } - @Test @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. fun testStateFragment_selectionInteraction_ratioButton_retainStateOnConfigurationChange() { From d271f859055a7ba8e73fead7dcf2bc993089dbca Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 1 Nov 2022 01:45:53 +0530 Subject: [PATCH 26/76] Added proto to store dragAndDrop sort --- .../DragAndDropSortInteractionViewModel.kt | 64 +++++++------------ model/src/main/proto/exploration.proto | 8 +++ 2 files changed, 30 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 9be34d05337..282724761cd 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.GroupSubtitledHtml /** [StateItemViewModel] for drag drop & sort choice list. */ class DragAndDropSortInteractionViewModel private constructor( @@ -45,15 +46,11 @@ class DragAndDropSortInteractionViewModel private constructor( interaction.customizationArgsMap["allowMultipleItemsInSamePosition"]?.boolValue ?: false } private val choiceSubtitledHtmls: List by lazy { - if (rawUserAnswer.hasDragAndDrop()) { - rawUserAnswer.dragAndDrop.listOfSubtitledHtmlsList - } else { interaction.customizationArgsMap["choices"] ?.schemaObjectList ?.schemaObjectList ?.map { schemaObject -> schemaObject.customSchemaValue.subtitledHtml } ?: listOf() - } } private val contentIdHtmlMap: Map = @@ -80,10 +77,7 @@ class DragAndDropSortInteractionViewModel private constructor( ) } } - Log.d("testAnswer", "choiceSubtitledHtmls: $choiceSubtitledHtmls\n ") - if (rawUserAnswer != RawUserAnswer.getDefaultInstance()) { - Log.d("testAnswer", rawUserAnswer.dragAndDrop.listOfSubtitledHtmlsOrBuilderList.toString()) - } + Log.d("TAGG", "INIT: "+rawUserAnswer.dragAndDrop.listOfGroupSubtitledHtmlList) isAnswerAvailable.addOnPropertyChangedCallback(callback) isAnswerAvailable.set(true) // For drag drop submit button will be enabled by default. } @@ -142,43 +136,29 @@ class DragAndDropSortInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - val htmlContentIds = _choiceItems.map { it.htmlContent } - val htmlContent = _choiceItems.map { it.computeStringList() } - val listofHtmlContentIds = mutableListOf() - val listofHtmlContent = mutableListOf() - htmlContentIds.forEach { - if (it.contentIdsCount > 1) { - it.contentIdsList.forEach { - listofHtmlContentIds.add(it.contentId) - } - } else { - listofHtmlContentIds.add(it.contentIdsList[0].contentId) - } - } - htmlContent.forEach { - if (it.htmlCount > 1) { - it.htmlList.forEach { - listofHtmlContent.add(it) - } - } else { - listofHtmlContent.add(it.htmlList.first().toString()) - } - } - val listofSubtitledHtml = listofHtmlContentIds.zip(listofHtmlContent) - Log.d("TAGG", "getRawUserAnswer: listOfSubtitledHtml $_choiceItems") - val SubtitleHtmlList = mutableListOf() + val htmlContentIds = _choiceItems.map { it.htmlContent.contentIdsList.toList() } + val htmlContent = _choiceItems.map { it.computeStringList().htmlList.toList() } + val listofSubtitledHtml = htmlContentIds.zip(htmlContent) + val GroupSubtitleHtmlList = mutableListOf() listofSubtitledHtml.forEach { - SubtitleHtmlList.add( - SubtitledHtml.newBuilder().apply { - contentId = it.first - html = it.second - }.build() + val newListofSubtitleHtml = mutableListOf() + it.first.zip(it.second).forEach { + newListofSubtitleHtml.add( + SubtitledHtml.newBuilder().apply { + contentId = it.first.contentId + html = it.second + }.build() + ) + } + GroupSubtitleHtmlList.add( + GroupSubtitledHtml.newBuilder() + .addAllListOfSubtitledHtmls(newListofSubtitleHtml) + .build() ) } - Log.d("TAGG", "getRawUserAnswer: SubtitleHtmlList " + SubtitleHtmlList.toString()) - dragAndDrop = DragAndDropRawAnswer.newBuilder().apply { - addAllListOfSubtitledHtmls(SubtitleHtmlList) - }.build() + dragAndDrop = DragAndDropRawAnswer.newBuilder() + .addAllListOfGroupSubtitledHtml(GroupSubtitleHtmlList) + .build() }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index cdf05a907dd..49e92c10c06 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -342,7 +342,15 @@ message RawUserAnswer { } } +// Corresponds to a current drag and drop answer message DragAndDropRawAnswer { + // list of group subtitled html + repeated GroupSubtitledHtml list_of_group_subtitled_html = 1; +} + +// A list of subtitled html +message GroupSubtitledHtml { + // list of subtitled html repeated SubtitledHtml list_of_subtitled_htmls = 1; } From 6910fb796d0371f3ce6a62979d0a4d74ba7079d4 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 1 Nov 2022 01:46:38 +0530 Subject: [PATCH 27/76] Optimized code --- .../DragAndDropSortInteractionViewModel.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 282724761cd..fd09a8482f0 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.app.model.DragAndDropRawAnswer +import org.oppia.android.app.model.GroupSubtitledHtml import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -25,7 +26,6 @@ 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.GroupSubtitledHtml /** [StateItemViewModel] for drag drop & sort choice list. */ class DragAndDropSortInteractionViewModel private constructor( @@ -46,11 +46,11 @@ class DragAndDropSortInteractionViewModel private constructor( interaction.customizationArgsMap["allowMultipleItemsInSamePosition"]?.boolValue ?: false } private val choiceSubtitledHtmls: List by lazy { - interaction.customizationArgsMap["choices"] - ?.schemaObjectList - ?.schemaObjectList - ?.map { schemaObject -> schemaObject.customSchemaValue.subtitledHtml } - ?: listOf() + interaction.customizationArgsMap["choices"] + ?.schemaObjectList + ?.schemaObjectList + ?.map { schemaObject -> schemaObject.customSchemaValue.subtitledHtml } + ?: listOf() } private val contentIdHtmlMap: Map = @@ -77,7 +77,7 @@ class DragAndDropSortInteractionViewModel private constructor( ) } } - Log.d("TAGG", "INIT: "+rawUserAnswer.dragAndDrop.listOfGroupSubtitledHtmlList) + Log.d("TAGG", "INIT: " + rawUserAnswer.dragAndDrop.listOfGroupSubtitledHtmlList) isAnswerAvailable.addOnPropertyChangedCallback(callback) isAnswerAvailable.set(true) // For drag drop submit button will be enabled by default. } From 3d4cc10a4348bd6d4f03a49c5f8922d7533389a8 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 1 Nov 2022 17:38:26 +0530 Subject: [PATCH 28/76] SelectionInteraction bug fixed of clearing selection --- .../app/player/state/StatePlayerRecyclerViewAssembler.kt | 1 + .../state/itemviewmodel/ContinueInteractionViewModel.kt | 1 + .../itemviewmodel/DragAndDropSortInteractionViewModel.kt | 1 + .../state/itemviewmodel/FractionInteractionViewModel.kt | 1 + .../ImageRegionSelectionInteractionViewModel.kt | 1 + .../itemviewmodel/MathExpressionInteractionsViewModel.kt | 1 + .../player/state/itemviewmodel/NumericInputViewModel.kt | 1 + .../RatioExpressionInputInteractionViewModel.kt | 1 + .../state/itemviewmodel/SelectionInteractionViewModel.kt | 8 ++++++-- .../app/player/state/itemviewmodel/StateItemViewModel.kt | 1 + .../app/player/state/itemviewmodel/TextInputViewModel.kt | 1 + .../app/testing/InputInteractionViewTestActivity.kt | 1 + 12 files changed, 17 insertions(+), 2 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 ba5d7da5960..7aa8484339a 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 @@ -315,6 +315,7 @@ class StatePlayerRecyclerViewAssembler private constructor( hasConversationView, rawUserAnswer, interaction, + canSubmitAnswer?.get() ?: false, fragment as InteractionAnswerReceiver, fragment as InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton, 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 f43189bbc7c..54468484c6f 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 @@ -56,6 +56,7 @@ class ContinueInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 fd09a8482f0..16175a23fbb 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 @@ -232,6 +232,7 @@ class DragAndDropSortInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 1c698e772d3..e2c90a25ce2 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 @@ -146,6 +146,7 @@ class FractionInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 80decef7439..7578b90d66e 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 @@ -111,6 +111,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 e26b2c3d7ff..d233d9c65a5 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 @@ -237,6 +237,7 @@ class MathExpressionInteractionsViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 112928ea665..2b7adbbf381 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 @@ -109,6 +109,7 @@ class NumericInputViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 62f12344e3a..7c7e58e70a1 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 @@ -147,6 +147,7 @@ class RatioExpressionInputInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 01accd57f34..d56f64ea03f 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.databinding.Observable import androidx.databinding.ObservableField import androidx.databinding.ObservableList @@ -31,6 +32,7 @@ class SelectionInteractionViewModel private constructor( val hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, val writtenTranslationContext: WrittenTranslationContext, @@ -74,12 +76,12 @@ class SelectionInteractionViewModel private constructor( ) } } - if (selectedAnswer.size == 1) { + if (selectedAnswer.size == 1 && isSubmitAnswerEnabled) { val selectedIndex = selectedAnswer[0] val isAnswerUpdated = updateSelection(selectedIndex, choiceItems[selectedIndex].isAnswerSelected.get()) choiceItems[selectedIndex].isAnswerSelected.set(isAnswerUpdated) - } else if (selectedAnswer.size > 1) { + } else if (selectedAnswer.size > 1 && isSubmitAnswerEnabled) { selectedAnswer.forEach { index -> val isAnswerUpdated = updateSelection(index, choiceItems[index].isAnswerSelected.get()) @@ -197,6 +199,7 @@ class SelectionInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, @@ -208,6 +211,7 @@ class SelectionInteractionViewModel private constructor( hasConversationView, rawUserAnswer, interaction, + isSubmitAnswerEnabled, answerErrorReceiver, isSplitView, writtenTranslationContext, 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 034c6d6229e..fcde65498c7 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 @@ -50,6 +50,7 @@ abstract class StateItemViewModel(val viewType: ViewType) : ObservableViewModel( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 70bdeda7984..dc30aeebdef 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 @@ -113,6 +113,7 @@ class TextInputViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 9bd66235064..7ccd689d750 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 @@ -161,6 +161,7 @@ class InputInteractionViewTestActivity : hasConversationView = false, rawUserAnswer = RawUserAnswer.getDefaultInstance(), interaction = interaction, + isSubmitAnswerEnabled = false, interactionAnswerReceiver = this@InputInteractionViewTestActivity, answerErrorReceiver = this@InputInteractionViewTestActivity, hasPreviousButton = false, From 69a418180ae286ada7566f35fabdf7c77eb8e27e Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 3 Nov 2022 01:07:31 +0530 Subject: [PATCH 29/76] Updated proto for DragAndDrop and added more tests --- .../player/state/StateFragmentPresenter.kt | 3 + .../DragAndDropSortInteractionViewModel.kt | 102 ++++++++++-------- ...mageRegionSelectionInteractionViewModel.kt | 7 +- .../SelectionInteractionViewModel.kt | 1 - .../app/utility/ClickableAreasImage.kt | 4 - .../app/player/state/StateFragmentTest.kt | 27 ++++- model/src/main/proto/exploration.proto | 14 +-- .../PlatformParameterConstants.kt | 4 +- 8 files changed, 92 insertions(+), 70 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 f66ff02f622..3ab4adf30f7 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 @@ -1,6 +1,7 @@ package org.oppia.android.app.player.state import android.content.Context +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -461,6 +462,7 @@ class StateFragmentPresenter @Inject constructor( fun getExplorationCheckpointState() = explorationCheckpointState fun getRawUserAnswer(): RawUserAnswer { + Log.d("TAGG", "getRawUserAnswer: " + isConfigChangeStateRetentionEnabled.value) return if (isConfigChangeStateRetentionEnabled.value) { viewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) } else RawUserAnswer.getDefaultInstance() @@ -509,6 +511,7 @@ class StateFragmentPresenter @Inject constructor( private fun setHintOpenedAndUnRevealed(isHintUnrevealed: Boolean) { viewModel.setHintOpenedAndUnRevealedVisibility(isHintUnrevealed) + Log.d("TAGG", "setHintOpenedAndUnRevealed: " + isHintBulbAnimationEnabled.value) if (isHintBulbAnimationEnabled.value) { if (isHintUnrevealed) { val hintBulbAnimation = AnimationUtils.loadAnimation( 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 16175a23fbb..00b00d322b9 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -1,11 +1,8 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.recyclerview.widget.RecyclerView -import org.oppia.android.app.model.DragAndDropRawAnswer -import org.oppia.android.app.model.GroupSubtitledHtml import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -61,7 +58,13 @@ class DragAndDropSortInteractionViewModel private constructor( } private val _choiceItems: MutableList = - computeChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler) + computeChoiceItems( + contentIdHtmlMap, + choiceSubtitledHtmls, + this, + resourceHandler, + rawUserAnswer.listOfSetsOfTranslatableHtmlContentIds + ) val choiceItems: List = _choiceItems @@ -77,7 +80,6 @@ class DragAndDropSortInteractionViewModel private constructor( ) } } - Log.d("TAGG", "INIT: " + rawUserAnswer.dragAndDrop.listOfGroupSubtitledHtmlList) isAnswerAvailable.addOnPropertyChangedCallback(callback) isAnswerAvailable.set(true) // For drag drop submit button will be enabled by default. } @@ -136,29 +138,11 @@ class DragAndDropSortInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - val htmlContentIds = _choiceItems.map { it.htmlContent.contentIdsList.toList() } - val htmlContent = _choiceItems.map { it.computeStringList().htmlList.toList() } - val listofSubtitledHtml = htmlContentIds.zip(htmlContent) - val GroupSubtitleHtmlList = mutableListOf() - listofSubtitledHtml.forEach { - val newListofSubtitleHtml = mutableListOf() - it.first.zip(it.second).forEach { - newListofSubtitleHtml.add( - SubtitledHtml.newBuilder().apply { - contentId = it.first.contentId - html = it.second - }.build() - ) - } - GroupSubtitleHtmlList.add( - GroupSubtitledHtml.newBuilder() - .addAllListOfSubtitledHtmls(newListofSubtitleHtml) - .build() - ) - } - dragAndDrop = DragAndDropRawAnswer.newBuilder() - .addAllListOfGroupSubtitledHtml(GroupSubtitleHtmlList) - .build() + val htmlContentIds = _choiceItems.map { it.htmlContent } + listOfSetsOfTranslatableHtmlContentIds = + ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply { + addAllContentIdLists(htmlContentIds) + }.build() }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ @@ -258,24 +242,54 @@ class DragAndDropSortInteractionViewModel private constructor( contentIdHtmlMap: Map, choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler + resourceHandler: AppLanguageResourceHandler, + listOfSetsOfTranslatableHtmlContentIds: ListOfSetsOfTranslatableHtmlContentIds ): MutableList { - return choiceStrings.mapIndexed { index, subtitledHtml -> - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = subtitledHtml.contentId - } - ) - }.build(), - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler + val listOfDragDropInteractionContentViewModel = + mutableListOf() + choiceStrings.mapIndexed { index, subtitledHtml -> + listOfDragDropInteractionContentViewModel.add( + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = subtitledHtml.contentId + } + ) + }.build(), + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) ) - }.toMutableList() + } + listOfSetsOfTranslatableHtmlContentIds.contentIdListsList.mapIndexed { itemIndex, contentId -> + if (contentId.contentIdsCount == 1) { + listOfDragDropInteractionContentViewModel[itemIndex].htmlContent = + SetOfTranslatableHtmlContentIds.newBuilder() + .addAllContentIds(contentId.contentIdsList) + .build() + } else if (contentId.contentIdsCount > 1) { + val item = listOfDragDropInteractionContentViewModel[itemIndex] + val nextItem = listOfDragDropInteractionContentViewModel[itemIndex + 1] + nextItem.htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addAllContentIds(nextItem.htmlContent.contentIdsList) + addAllContentIds(item.htmlContent.contentIdsList) + }.build() + listOfDragDropInteractionContentViewModel[itemIndex + 1] = nextItem + + listOfDragDropInteractionContentViewModel.removeAt(itemIndex) + + listOfDragDropInteractionContentViewModel.forEachIndexed { index, viewModel -> + viewModel.itemIndex = index + viewModel.listSize = + listOfDragDropInteractionContentViewModel.size + } + } + } + return listOfDragDropInteractionContentViewModel } } } 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 7578b90d66e..8da66c75c89 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 @@ -7,7 +7,6 @@ import org.oppia.android.app.model.ClickOnImage import org.oppia.android.app.model.ImageWithRegions import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject -import org.oppia.android.app.model.Point2d import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext @@ -94,11 +93,9 @@ class ImageRegionSelectionInteractionViewModel private constructor( private fun parseClickOnImage(answerTextString: String): ClickOnImage { val region = selectableRegions.find { it.label == answerTextString } return ClickOnImage.newBuilder().apply { + // The object supports multiple regions in an answer, but neither web nor Android + // supports this. addClickedRegions(region?.label ?: "") - clickPosition = Point2d.newBuilder().apply { - x = region?.region?.area?.upperLeft?.x ?: 0F - y = region?.region?.area?.lowerRight?.y ?: 0F - }.build() }.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 d56f64ea03f..b70a4ad02fa 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,6 +1,5 @@ package org.oppia.android.app.player.state.itemviewmodel -import android.util.Log import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.databinding.ObservableList diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index c461d62a484..6b67f441eba 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -1,7 +1,6 @@ package org.oppia.android.app.utility import android.graphics.RectF -import android.util.Log import android.view.MotionEvent import android.view.View import android.widget.FrameLayout @@ -94,7 +93,6 @@ class ClickableAreasImage( getXCoordinate(clickableArea.region.area.lowerRight.x), getYCoordinate(clickableArea.region.area.lowerRight.y) ) - Log.d("TAGG", "addRegionViews: " + clickableArea.region.area.upperLeft) val layoutParams = FrameLayout.LayoutParams( imageRect.width().roundToInt(), imageRect.height().roundToInt() @@ -144,8 +142,6 @@ class ClickableAreasImage( } } - // Saturn - // You have selected planet Saturn fun showOrHideRegion(newView: View, clickableArea: ImageWithRegions.LabeledRegion) { resetRegionSelectionViews() listener.onClickableAreaTouched( 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 bd2d4634a90..55c9b69c8de 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 @@ -1599,8 +1599,8 @@ class StateFragmentTest { } @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. fun testStateFragment_fractionInput_retainStateOnConfigurationChange() { - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() clickContinueInteractionButton() @@ -1618,6 +1618,7 @@ class StateFragmentTest { } @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. fun testStateFragment_numericInput_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { startPlayingExploration() @@ -1640,6 +1641,7 @@ class StateFragmentTest { } @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. fun testStateFragment_ratioInput_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { startPlayingExploration() @@ -1663,6 +1665,7 @@ class StateFragmentTest { } @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. fun testStateFragment_textInput_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { startPlayingExploration() @@ -1686,6 +1689,7 @@ class StateFragmentTest { } @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. fun testStateFragment_mathInteractions_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { startPlayingExploration() @@ -1796,6 +1800,27 @@ class StateFragmentTest { } } + @Test + @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + fun testStateFragment_dragAndDropSort_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { + startPlayingExploration() + + mergeDragAndDropItems(position = 0) + + // Rotating device. + rotateToLandscape() + scrollToViewType(DRAG_DROP_SORT_INTERACTION) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 0, + targetViewId = R.id.drag_drop_item_recyclerview + ) + ).check(matches(hasChildCount(2))) + } + } + @Test fun testStateFragment_ratioInput_textViewHasTextInputType() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { scenario -> diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 49e92c10c06..fe74471ae15 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -336,24 +336,12 @@ message RawUserAnswer { // A user's selection for item selection and multiple choice interactions. ItemSelectionRawAnswer item_selection = 3; // A user's selection for drag and drop interactions. - DragAndDropRawAnswer drag_and_drop = 4; + ListOfSetsOfTranslatableHtmlContentIds list_of_sets_of_translatable_html_content_ids = 4; // A user's selection for image region selection interaction. ImageWithRegions.LabeledRegion image_region_selection = 5; } } -// Corresponds to a current drag and drop answer -message DragAndDropRawAnswer { - // list of group subtitled html - repeated GroupSubtitledHtml list_of_group_subtitled_html = 1; -} - -// A list of subtitled html -message GroupSubtitledHtml { - // list of subtitled html - repeated SubtitledHtml list_of_subtitled_htmls = 1; -} - message AnswerAndResponse { // A previous answer the learner submitted. UserAnswer user_answer = 1; diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index 7810bfcbe38..b5f85e09c47 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -215,7 +215,7 @@ const val ENABLE_SPOTLIGHT_UI_DEFAULT_VALUE = true annotation class EnableInteractionConfigChangeStateRetention /** Default value for feature flag corresponding to [EnableInteractionConfigChangeStateRetention]. */ -const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true +const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = false /** * Qualifier for the platform parameter that controls the animation for hint bulb animation @@ -224,4 +224,4 @@ const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true annotation class EnableHintBulbAnimation /** Default value for feature flag corresponding to [EnableHintBulbAnimation]. */ -const val ENABLE_HINT_BULB_ANIMATION = false +const val ENABLE_HINT_BULB_ANIMATION = true From db30ea2a9303373b15687ca7d6aab97382b7891c Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 3 Nov 2022 18:46:09 +0530 Subject: [PATCH 30/76] Optimized code --- .../ImageRegionSelectionInteractionView.kt | 2 +- .../player/state/StateFragmentPresenter.kt | 1 - .../SelectionInteractionViewModel.kt | 21 +++------- .../app/utility/ClickableAreasImage.kt | 42 ++++++------------- .../app/player/state/StateFragmentTest.kt | 20 ++++----- .../PlatformParameterConstants.kt | 2 +- 6 files changed, 30 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index 03ede90c4a0..c944910d5ab 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -127,7 +127,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( ) areasImage.addRegionViews() if (lastSelectedRegion.hasRegion()) { - areasImage.highlightBox(lastSelectedRegion) + areasImage.showOrHideRegion(lastSelectedRegion) } performAttachment(areasImage) } 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 3ab4adf30f7..81cf2a00627 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 @@ -511,7 +511,6 @@ class StateFragmentPresenter @Inject constructor( private fun setHintOpenedAndUnRevealed(isHintUnrevealed: Boolean) { viewModel.setHintOpenedAndUnRevealedVisibility(isHintUnrevealed) - Log.d("TAGG", "setHintOpenedAndUnRevealed: " + isHintBulbAnimationEnabled.value) if (isHintBulbAnimationEnabled.value) { if (isHintUnrevealed) { val hintBulbAnimation = AnimationUtils.loadAnimation( 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 b70a4ad02fa..949e358c62b 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 @@ -56,7 +56,7 @@ class SelectionInteractionViewModel private constructor( ?: minAllowableSelectionCount } - val selectedItems: MutableList = mutableListOf() + private val selectedItems: MutableList = mutableListOf() private val selectedAnswer: MutableList = rawUserAnswer.itemSelection.selectedIndexesList ?: mutableListOf() @@ -75,16 +75,11 @@ class SelectionInteractionViewModel private constructor( ) } } - if (selectedAnswer.size == 1 && isSubmitAnswerEnabled) { - val selectedIndex = selectedAnswer[0] - val isAnswerUpdated = - updateSelection(selectedIndex, choiceItems[selectedIndex].isAnswerSelected.get()) - choiceItems[selectedIndex].isAnswerSelected.set(isAnswerUpdated) - } else if (selectedAnswer.size > 1 && isSubmitAnswerEnabled) { + if (selectedAnswer.size > 0 && isSubmitAnswerEnabled) { selectedAnswer.forEach { index -> - val isAnswerUpdated = - updateSelection(index, choiceItems[index].isAnswerSelected.get()) - choiceItems[index].isAnswerSelected.set(isAnswerUpdated) + selectedItems += index + updateIsAnswerAvailable() + choiceItems[index].isAnswerSelected.set(true) } } isAnswerAvailable.addOnPropertyChangedCallback(callback) @@ -120,11 +115,7 @@ class SelectionInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { itemSelection = ItemSelectionRawAnswer.newBuilder().apply { - if (interactionId == "ItemSelectionInput") { - addAllSelectedIndexes(selectedItems) - } else if (selectedItems.size == 1) { - addSelectedIndexes(selectedItems.first()) - } + addAllSelectedIndexes(selectedItems) }.build() }.build() diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index 6b67f441eba..5c6166c3c05 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -87,32 +87,10 @@ class ClickableAreasImage( it.id != imageView.id && it.id != defaultRegionView.id }.forEach(parentView::removeView) clickableAreas.forEach { clickableArea -> - val imageRect = RectF( - getXCoordinate(clickableArea.region.area.upperLeft.x), - getYCoordinate(clickableArea.region.area.upperLeft.y), - getXCoordinate(clickableArea.region.area.lowerRight.x), - getYCoordinate(clickableArea.region.area.lowerRight.y) - ) - val layoutParams = FrameLayout.LayoutParams( - imageRect.width().roundToInt(), - imageRect.height().roundToInt() - ) - val newView = View(parentView.context) - // ClickableArea coordinates are not laid-out properly in RTL. The image region coordinates - // are from left-to-right with an upper left origin and touch coordinates from Android start - // from the right in RTL mode. Thus, to avoid this situation, force layout direction to LTR in - // all situations. - ViewCompat.setLayoutDirection(parentView, ViewCompat.LAYOUT_DIRECTION_LTR) - newView.layoutParams = layoutParams - newView.x = imageRect.left - newView.y = imageRect.top - newView.isClickable = true - newView.isFocusable = true - newView.isFocusableInTouchMode = true - newView.tag = clickableArea.label + val newView = createSelectableView(clickableArea) newView.setOnTouchListener { _, event -> if (event.action == MotionEvent.ACTION_DOWN) { - showOrHideRegion(newView, clickableArea) + showOrHideRegion(clickableArea) } return@setOnTouchListener true } @@ -120,11 +98,9 @@ class ClickableAreasImage( // Make default region visibility gone when talkback enabled to avoid any accidental touch. defaultRegionView.isVisible = false newView.setOnClickListener { - showOrHideRegion(newView, clickableArea) + showOrHideRegion(clickableArea) } } - newView.contentDescription = clickableArea.contentDescription - parentView.addView(newView) } // Ensure that the children views are properly computed. The specific flow below is recommended @@ -142,7 +118,12 @@ class ClickableAreasImage( } } - fun showOrHideRegion(newView: View, clickableArea: ImageWithRegions.LabeledRegion) { + /** + * Called to show or hide selected image region + * + * @param clickableArea a clickable image region which we want to show or hide + */ + fun showOrHideRegion(clickableArea: ImageWithRegions.LabeledRegion) { resetRegionSelectionViews() listener.onClickableAreaTouched( NamedRegionClickedEvent( @@ -150,10 +131,11 @@ class ClickableAreasImage( clickableArea.contentDescription ) ) + val newView = createSelectableView(clickableArea) newView.setBackgroundResource(R.drawable.selected_region_background) } - fun highlightBox(clickableArea: ImageWithRegions.LabeledRegion) { + private fun createSelectableView(clickableArea: ImageWithRegions.LabeledRegion): View { val imageRect = RectF( getXCoordinate(clickableArea.region.area.upperLeft.x), getYCoordinate(clickableArea.region.area.upperLeft.y), @@ -177,6 +159,6 @@ class ClickableAreasImage( newView.contentDescription = clickableArea.contentDescription parentView.addView(newView) - showOrHideRegion(newView, clickableArea) + return newView } } 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 55c9b69c8de..a13a8ef51c9 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 @@ -1857,9 +1857,9 @@ class StateFragmentTest { ) } -// TODO(#503): Add versions of the following multi-language & localization tests for questions. + // TODO(#503): Add versions of the following multi-language & localization tests for questions. -/* Multi-language & localization tests. */ + /* Multi-language & localization tests. */ @Test @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso. @@ -3813,8 +3813,8 @@ class StateFragmentTest { } } -// TODO(#3171): Implement image region selection tests for English/Arabic to demonstrate that -// answers submit normally & with no special behaviors. + // TODO(#3171): Implement image region selection tests for English/Arabic to demonstrate that + // answers submit normally & with no special behaviors. @Test fun testStateFragment_clickContinue_returnToState_doesNotHaveFeedbackBox() { @@ -4382,12 +4382,12 @@ class StateFragmentTest { return ApplicationProvider.getApplicationContext().isOnRobolectric() } -// TODO(#59): Remove these waits once we can ensure that the production executors are not depended on in tests. -// Sleeping is really bad practice in Espresso tests, and can lead to test flakiness. It shouldn't be necessary if we -// use a test executor service with a counting idle resource, but right now Gradle mixes dependencies such that both -// the test and production blocking executors are being used. The latter cannot be updated to notify Espresso of any -// active coroutines, so the test attempts to assert state before it's ready. This artificial delay in the Espresso -// thread helps to counter that. + // TODO(#59): Remove these waits once we can ensure that the production executors are not depended on in tests. + // Sleeping is really bad practice in Espresso tests, and can lead to test flakiness. It shouldn't be necessary if we + // use a test executor service with a counting idle resource, but right now Gradle mixes dependencies such that both + // the test and production blocking executors are being used. The latter cannot be updated to notify Espresso of any + // active coroutines, so the test attempts to assert state before it's ready. This artificial delay in the Espresso + // thread helps to counter that. /** * Perform action of waiting for a specific matcher to finish. Adapted from: * https://stackoverflow.com/a/22563297/3689782. diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index b5f85e09c47..ec5afdd9470 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -215,7 +215,7 @@ const val ENABLE_SPOTLIGHT_UI_DEFAULT_VALUE = true annotation class EnableInteractionConfigChangeStateRetention /** Default value for feature flag corresponding to [EnableInteractionConfigChangeStateRetention]. */ -const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = false +const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true /** * Qualifier for the platform parameter that controls the animation for hint bulb animation From c8ec8ee3fc0981df24cc0b129492cb6954eab66f Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 3 Nov 2022 18:48:11 +0530 Subject: [PATCH 31/76] Fixed static checks --- .../testing/platformparameter/TestPlatformParameterModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt index a408ac0275d..3f383bb4875 100644 --- a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt +++ b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt @@ -254,7 +254,7 @@ class TestPlatformParameterModule { fun forceEnableHintBulbAnimation(value: Boolean) { enableInteractionConfigChangeStateRetention = value } - + /** Enables forcing [EnablePerformanceMetricsCollection] platform parameter flag from tests. */ @VisibleForTesting(otherwise = VisibleForTesting.NONE) fun forceEnablePerformanceMetricsCollection(value: Boolean) { From 031b5a555ce5116ee7a573092bbe1697bef81a4b Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 3 Nov 2022 20:45:58 +0530 Subject: [PATCH 32/76] Seperated text-based interactions --- .../FractionInteractionViewModel.kt | 4 ++-- .../MathExpressionInteractionsViewModel.kt | 4 ++-- .../itemviewmodel/NumericInputViewModel.kt | 4 ++-- ...RatioExpressionInputInteractionViewModel.kt | 4 ++-- .../state/itemviewmodel/TextInputViewModel.kt | 4 ++-- model/src/main/proto/exploration.proto | 18 +++++++++++++----- .../PlatformParameterConstants.kt | 2 +- 7 files changed, 24 insertions(+), 16 deletions(-) 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 e2c90a25ce2..b1646ba7f22 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,7 +32,7 @@ class FractionInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.FRACTION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.fraction ?: "" var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") val hintText: CharSequence = deriveHintText(interaction) @@ -88,7 +88,7 @@ class FractionInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - textualAnswer = answerText.toString() + fraction = answerText.toString() } }.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 d233d9c65a5..b2b8347706d 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 = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.mathExpression ?: "" /** * Defines whether an answer is currently available to parse. This is expected to be directly @@ -146,7 +146,7 @@ class MathExpressionInteractionsViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - textualAnswer = answerText.toString() + mathExpression = answerText.toString() } }.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 2b7adbbf381..d4fdaf450e8 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,7 +26,7 @@ class NumericInputViewModel private constructor( private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler ) : StateItemViewModel(ViewType.NUMERIC_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.numeric ?: "" private var pendingAnswerError: String? = null val errorMessage = ObservableField("") var isAnswerAvailable = ObservableField(false) @@ -96,7 +96,7 @@ class NumericInputViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - textualAnswer = answerText.toString() + numeric = answerText.toString() } }.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 7c7e58e70a1..a9435023a22 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,7 +33,7 @@ class RatioExpressionInputInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.RATIO_EXPRESSION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.ratioInput ?: "" var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") @@ -73,7 +73,7 @@ class RatioExpressionInputInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - textualAnswer = answerText.toString() + ratioInput = answerText.toString() } }.build() 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 dc30aeebdef..330f5084297 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 @@ -28,7 +28,7 @@ class TextInputViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController ) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.text ?: "" val hintText: CharSequence = deriveHintText(interaction) var isAnswerAvailable = ObservableField(false) @@ -78,7 +78,7 @@ class TextInputViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - textualAnswer = answerText.toString() + text = answerText.toString() } }.build() diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index fe74471ae15..9e447b630c4 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -331,14 +331,22 @@ message ItemSelectionRawAnswer { // retain state on configuration changes. message RawUserAnswer { oneof answer_input_type { - // A raw answer entered by user in a text-based interactions. - string textual_answer = 1; + // A raw answer entered by user in text input interaction. + string text = 1; + // A raw answer entered by user in fraction input interaction. + string fraction = 2; + // A raw answer entered by user in numeric input interaction. + string numeric = 3; + // A raw answer entered by user in ratio input interaction. + string ratio_input = 4; + // A raw answer entered by user in ratio input interaction. + string math_expression = 5; // A user's selection for item selection and multiple choice interactions. - ItemSelectionRawAnswer item_selection = 3; + ItemSelectionRawAnswer item_selection = 6; // A user's selection for drag and drop interactions. - ListOfSetsOfTranslatableHtmlContentIds list_of_sets_of_translatable_html_content_ids = 4; + ListOfSetsOfTranslatableHtmlContentIds list_of_sets_of_translatable_html_content_ids = 7; // A user's selection for image region selection interaction. - ImageWithRegions.LabeledRegion image_region_selection = 5; + ImageWithRegions.LabeledRegion image_region_selection = 8; } } diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index ec5afdd9470..b5f85e09c47 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -215,7 +215,7 @@ const val ENABLE_SPOTLIGHT_UI_DEFAULT_VALUE = true annotation class EnableInteractionConfigChangeStateRetention /** Default value for feature flag corresponding to [EnableInteractionConfigChangeStateRetention]. */ -const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true +const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = false /** * Qualifier for the platform parameter that controls the animation for hint bulb animation From 42987e6f9b9ad83a927d719a4ba069d0dd5caf84 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 4 Nov 2022 00:46:06 +0530 Subject: [PATCH 33/76] Enabled platform parameter and optimized code --- .../oppia/android/app/player/state/StateFragmentPresenter.kt | 5 ++--- .../util/platformparameter/PlatformParameterConstants.kt | 2 +- 2 files changed, 3 insertions(+), 4 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 81cf2a00627..4118b6c35c9 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 @@ -511,8 +511,8 @@ class StateFragmentPresenter @Inject constructor( private fun setHintOpenedAndUnRevealed(isHintUnrevealed: Boolean) { viewModel.setHintOpenedAndUnRevealedVisibility(isHintUnrevealed) - if (isHintBulbAnimationEnabled.value) { - if (isHintUnrevealed) { + if (!isHintBulbAnimationEnabled.value) + return if (isHintUnrevealed) { val hintBulbAnimation = AnimationUtils.loadAnimation( context, R.anim.hint_bulb_animation @@ -530,7 +530,6 @@ class StateFragmentPresenter @Inject constructor( } else { binding.hintBulb.clearAnimation() } - } } /** diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index b5f85e09c47..ec5afdd9470 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -215,7 +215,7 @@ const val ENABLE_SPOTLIGHT_UI_DEFAULT_VALUE = true annotation class EnableInteractionConfigChangeStateRetention /** Default value for feature flag corresponding to [EnableInteractionConfigChangeStateRetention]. */ -const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = false +const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION = true /** * Qualifier for the platform parameter that controls the animation for hint bulb animation From 10a91bd7219383ea00d15b224bc98b8988fbd189 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 4 Nov 2022 00:55:53 +0530 Subject: [PATCH 34/76] Added ToDo --- .../app/player/state/StateFragmentTest.kt | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 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 220480a1a65..d474917c692 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 @@ -1599,8 +1599,9 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_fractionInput_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() @@ -1618,8 +1619,8 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_numericInput_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { startPlayingExploration() @@ -1641,8 +1642,8 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_ratioInput_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { startPlayingExploration() @@ -1665,8 +1666,8 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_textInput_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use { startPlayingExploration() @@ -1689,8 +1690,8 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_mathInteractions_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { startPlayingExploration() @@ -1708,8 +1709,8 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_selectionInteraction_ratioButton_retainStateOnConfigurationChange() { TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { @@ -1731,8 +1732,8 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_selectionInteraction_multipleSelection_retainStateOnConfigurationChange() { TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { @@ -1772,8 +1773,8 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_dragAndDrop_retainStateOnConfigurationChange() { TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { @@ -1801,8 +1802,8 @@ class StateFragmentTest { } } - @Test - @RunOn(TestPlatform.ESPRESSO) // TODO(#1612): Enable for Robolectric. + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_dragAndDropSort_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { startPlayingExploration() From 427898ba2864c70e5c4f4119c3e7652828f1485a Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 4 Nov 2022 00:58:49 +0530 Subject: [PATCH 35/76] Removed extra line --- .../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 d474917c692..8737c084c00 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 @@ -1599,7 +1599,6 @@ class StateFragmentTest { } } - @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_fractionInput_retainStateOnConfigurationChange() { From 45bafdd6a136c5071c9eaba5625934daf5ef778c Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 5 Nov 2022 02:27:56 +0530 Subject: [PATCH 36/76] Fixed issue with DragAndDropSortInteraction --- .../DragAndDropSortInteractionViewModel.kt | 104 +++++++++++++----- model/src/main/proto/exploration.proto | 14 ++- 2 files changed, 92 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 00b00d322b9..b5f567fe87b 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 @@ -23,6 +23,8 @@ 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.DragAndDropRawAnswer +import org.oppia.android.app.model.GroupSubtitledHtml /** [StateItemViewModel] for drag drop & sort choice list. */ class DragAndDropSortInteractionViewModel private constructor( @@ -30,6 +32,7 @@ class DragAndDropSortInteractionViewModel private constructor( val hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, + isSubmitAnswerEnabled: Boolean, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, private val writtenTranslationContext: WrittenTranslationContext, @@ -58,12 +61,17 @@ class DragAndDropSortInteractionViewModel private constructor( } private val _choiceItems: MutableList = - computeChoiceItems( + if (rawUserAnswer.hasDragAndDrop() && isSubmitAnswerEnabled) computeMergeChoiceItems( contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler, - rawUserAnswer.listOfSetsOfTranslatableHtmlContentIds + rawUserAnswer.dragAndDrop + ) else computeChoiceItems( + contentIdHtmlMap, + choiceSubtitledHtmls, + this, + resourceHandler ) val choiceItems: List = _choiceItems @@ -138,11 +146,29 @@ class DragAndDropSortInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - val htmlContentIds = _choiceItems.map { it.htmlContent } - listOfSetsOfTranslatableHtmlContentIds = - ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply { - addAllContentIdLists(htmlContentIds) - }.build() + val htmlContentIds = _choiceItems.map { it.htmlContent.contentIdsList.toList() } + val htmlContent = _choiceItems.map { it.computeStringList().htmlList.toList() } + val listofSubtitledHtml = htmlContentIds.zip(htmlContent) + val GroupSubtitleHtmlList = mutableListOf() + listofSubtitledHtml.forEach { + val newListofSubtitleHtml = mutableListOf() + it.first.zip(it.second).forEach { + newListofSubtitleHtml.add( + SubtitledHtml.newBuilder().apply { + contentId = it.first.contentId + html = it.second + }.build() + ) + } + GroupSubtitleHtmlList.add( + GroupSubtitledHtml.newBuilder() + .addAllListOfSubtitledHtmls(newListofSubtitleHtml) + .build() + ) + } + dragAndDrop = DragAndDropRawAnswer.newBuilder() + .addAllListOfGroupSubtitledHtml(GroupSubtitleHtmlList) + .build() }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ @@ -228,6 +254,7 @@ class DragAndDropSortInteractionViewModel private constructor( hasConversationView, rawUserAnswer, interaction, + isSubmitAnswerEnabled, answerErrorReceiver, isSplitView, writtenTranslationContext, @@ -243,7 +270,6 @@ class DragAndDropSortInteractionViewModel private constructor( choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, resourceHandler: AppLanguageResourceHandler, - listOfSetsOfTranslatableHtmlContentIds: ListOfSetsOfTranslatableHtmlContentIds ): MutableList { val listOfDragDropInteractionContentViewModel = mutableListOf() @@ -265,31 +291,59 @@ class DragAndDropSortInteractionViewModel private constructor( ) ) } - listOfSetsOfTranslatableHtmlContentIds.contentIdListsList.mapIndexed { itemIndex, contentId -> - if (contentId.contentIdsCount == 1) { - listOfDragDropInteractionContentViewModel[itemIndex].htmlContent = - SetOfTranslatableHtmlContentIds.newBuilder() - .addAllContentIds(contentId.contentIdsList) - .build() - } else if (contentId.contentIdsCount > 1) { - val item = listOfDragDropInteractionContentViewModel[itemIndex] - val nextItem = listOfDragDropInteractionContentViewModel[itemIndex + 1] + return listOfDragDropInteractionContentViewModel + } + + private fun computeMergeChoiceItems( + contentIdHtmlMap: Map, + choiceStrings: List, + dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, + resourceHandler: AppLanguageResourceHandler, + listOfGroupSubtitledHtml: DragAndDropRawAnswer + ): MutableList { + val listOfDragAndDropInteractionContentViewModel = + mutableListOf() + listOfGroupSubtitledHtml.listOfGroupSubtitledHtmlList.forEach { + it.listOfSubtitledHtmlsList.mapIndexed { index, subtitledHtml -> + listOfDragAndDropInteractionContentViewModel.add( + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addContentIds( + TranslatableHtmlContentId.newBuilder().apply { + contentId = subtitledHtml.contentId + } + ) + }.build(), + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + ) + } + } + listOfGroupSubtitledHtml.listOfGroupSubtitledHtmlList.mapIndexed { itemIndex, it -> + if (it.listOfSubtitledHtmlsCount > 1) { + val item = listOfDragAndDropInteractionContentViewModel[itemIndex] + val nextItem = listOfDragAndDropInteractionContentViewModel[itemIndex + 1] nextItem.htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addAllContentIds(nextItem.htmlContent.contentIdsList) addAllContentIds(item.htmlContent.contentIdsList) + addAllContentIds(nextItem.htmlContent.contentIdsList) }.build() - listOfDragDropInteractionContentViewModel[itemIndex + 1] = nextItem + listOfDragAndDropInteractionContentViewModel[itemIndex + 1] = nextItem - listOfDragDropInteractionContentViewModel.removeAt(itemIndex) + listOfDragAndDropInteractionContentViewModel.removeAt(itemIndex) - listOfDragDropInteractionContentViewModel.forEachIndexed { index, viewModel -> - viewModel.itemIndex = index - viewModel.listSize = - listOfDragDropInteractionContentViewModel.size + listOfDragAndDropInteractionContentViewModel.forEachIndexed { index, dragDropInteractionContentViewModel -> + dragDropInteractionContentViewModel.itemIndex = index + dragDropInteractionContentViewModel.listSize = + listOfDragAndDropInteractionContentViewModel.size } } } - return listOfDragDropInteractionContentViewModel + return listOfDragAndDropInteractionContentViewModel } } } + diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 9e447b630c4..752924aabc3 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -344,12 +344,24 @@ message RawUserAnswer { // A user's selection for item selection and multiple choice interactions. ItemSelectionRawAnswer item_selection = 6; // A user's selection for drag and drop interactions. - ListOfSetsOfTranslatableHtmlContentIds list_of_sets_of_translatable_html_content_ids = 7; + DragAndDropRawAnswer drag_and_drop = 7; // A user's selection for image region selection interaction. ImageWithRegions.LabeledRegion image_region_selection = 8; } } +// Corresponds to a current drag and drop answer +message DragAndDropRawAnswer { + // list of group subtitled html + repeated GroupSubtitledHtml list_of_group_subtitled_html = 1; +} + +// A list of subtitled html +message GroupSubtitledHtml { + // list of subtitled html + repeated SubtitledHtml list_of_subtitled_htmls = 1; +} + message AnswerAndResponse { // A previous answer the learner submitted. UserAnswer user_answer = 1; From c56dd5b21b2fa29dda6cf3feee6f5406f8958f68 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 5 Nov 2022 02:28:45 +0530 Subject: [PATCH 37/76] Code optimized --- .../DragAndDropSortInteractionViewModel.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index b5f567fe87b..0d91cab88c3 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -3,6 +3,8 @@ package org.oppia.android.app.player.state.itemviewmodel import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.recyclerview.widget.RecyclerView +import org.oppia.android.app.model.DragAndDropRawAnswer +import org.oppia.android.app.model.GroupSubtitledHtml import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -23,8 +25,6 @@ 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.DragAndDropRawAnswer -import org.oppia.android.app.model.GroupSubtitledHtml /** [StateItemViewModel] for drag drop & sort choice list. */ class DragAndDropSortInteractionViewModel private constructor( @@ -335,9 +335,9 @@ class DragAndDropSortInteractionViewModel private constructor( listOfDragAndDropInteractionContentViewModel.removeAt(itemIndex) - listOfDragAndDropInteractionContentViewModel.forEachIndexed { index, dragDropInteractionContentViewModel -> - dragDropInteractionContentViewModel.itemIndex = index - dragDropInteractionContentViewModel.listSize = + listOfDragAndDropInteractionContentViewModel.forEachIndexed { index, viewModel -> + viewModel.itemIndex = index + viewModel.listSize = listOfDragAndDropInteractionContentViewModel.size } } @@ -346,4 +346,3 @@ class DragAndDropSortInteractionViewModel private constructor( } } } - From 7c5f71444016437c9720872cc6e0c75c643e4bc8 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 7 Nov 2022 00:55:21 +0530 Subject: [PATCH 38/76] New tests added and handled edge cases --- .../android/app/player/state/StateFragment.kt | 9 ++ .../player/state/StateFragmentPresenter.kt | 41 +++--- .../state/StatePlayerRecyclerViewAssembler.kt | 12 +- .../ContinueInteractionViewModel.kt | 1 - .../DragAndDropSortInteractionViewModel.kt | 117 ++++-------------- .../FractionInteractionViewModel.kt | 5 +- ...mageRegionSelectionInteractionViewModel.kt | 12 +- .../MathExpressionInteractionsViewModel.kt | 5 +- .../itemviewmodel/NumericInputViewModel.kt | 5 +- ...atioExpressionInputInteractionViewModel.kt | 5 +- .../SelectionInteractionViewModel.kt | 5 +- .../state/itemviewmodel/StateItemViewModel.kt | 1 - .../state/itemviewmodel/TextInputViewModel.kt | 5 +- .../InputInteractionViewTestActivity.kt | 1 - .../app/player/state/StateFragmentTest.kt | 62 ++++++++-- model/src/main/proto/exploration.proto | 32 +---- 16 files changed, 143 insertions(+), 175 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 f4c15429c83..04c014f9a44 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 @@ -26,6 +26,8 @@ import org.oppia.android.util.extensions.putProto import javax.inject.Inject private const val STATE_FRAGMENT_RAW_USER_ANSWER_KEY = "StateFragment.raw_user_answer" +private const val STATE_FRAGMENT_HAS_PREVIOUS_HEADER_COLLAPSED = + "StateFragment.has_response_header_collapsed" /** Fragment that represents the current state of an exploration. */ class StateFragment : @@ -87,6 +89,8 @@ class StateFragment : val rawUserAnswer = savedInstanceState?.getProto( STATE_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() ) ?: RawUserAnswer.getDefaultInstance() + val hasPreviousResponsesExpanded = + savedInstanceState?.getBoolean(STATE_FRAGMENT_HAS_PREVIOUS_HEADER_COLLAPSED) ?: false return stateFragmentPresenter.handleCreateView( inflater, container, @@ -94,6 +98,7 @@ class StateFragment : topicId, storyId, rawUserAnswer, + hasPreviousResponsesExpanded, explorationId ) } @@ -144,6 +149,10 @@ class StateFragment : STATE_FRAGMENT_RAW_USER_ANSWER_KEY, stateFragmentPresenter.getRawUserAnswer() ) + outState.putBoolean( + STATE_FRAGMENT_HAS_PREVIOUS_HEADER_COLLAPSED, + stateFragmentPresenter.getHasPreviousResponsesExpanded() + ) } fun revealSolution() = stateFragmentPresenter.revealSolution() 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 4118b6c35c9..1707ef4bb63 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 @@ -1,7 +1,6 @@ package org.oppia.android.app.player.state import android.content.Context -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -115,6 +114,7 @@ class StateFragmentPresenter @Inject constructor( topicId: String, storyId: String, rawUserAnswer: RawUserAnswer, + hasPreviousResponsesExpanded: Boolean, explorationId: String ): View? { profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() @@ -134,6 +134,8 @@ class StateFragmentPresenter @Inject constructor( binding.fullScreenConfettiView ) + recyclerViewAssembler.hasPreviousResponsesExpanded = hasPreviousResponsesExpanded + val stateRecyclerViewAdapter = recyclerViewAssembler.adapter val rhsStateRecyclerViewAdapter = recyclerViewAssembler.rhsAdapter binding.stateRecyclerView.apply { @@ -205,6 +207,7 @@ class StateFragmentPresenter @Inject constructor( } fun onSubmitButtonClicked() { + recyclerViewAssembler.resetRawUserAnswer() hideKeyboard() handleSubmitAnswer(viewModel.getPendingAnswer(recyclerViewAssembler::getPendingAnswerHandler)) } @@ -271,6 +274,10 @@ class StateFragmentPresenter @Inject constructor( subscribeToHintSolution(explorationProgressController.submitSolutionIsRevealed()) } + fun getHasPreviousResponsesExpanded(): Boolean { + return recyclerViewAssembler.hasPreviousResponsesExpanded + } + private fun getStateViewModel(): StateViewModel { return viewModelProvider.getForFragment(fragment, StateViewModel::class.java) } @@ -462,7 +469,6 @@ class StateFragmentPresenter @Inject constructor( fun getExplorationCheckpointState() = explorationCheckpointState fun getRawUserAnswer(): RawUserAnswer { - Log.d("TAGG", "getRawUserAnswer: " + isConfigChangeStateRetentionEnabled.value) return if (isConfigChangeStateRetentionEnabled.value) { viewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) } else RawUserAnswer.getDefaultInstance() @@ -511,25 +517,24 @@ class StateFragmentPresenter @Inject constructor( private fun setHintOpenedAndUnRevealed(isHintUnrevealed: Boolean) { viewModel.setHintOpenedAndUnRevealedVisibility(isHintUnrevealed) - if (!isHintBulbAnimationEnabled.value) - return if (isHintUnrevealed) { - val hintBulbAnimation = AnimationUtils.loadAnimation( - context, - R.anim.hint_bulb_animation - ).also { it.interpolator = BounceUpAndDownInterpolator() } - - // The bulb should start bouncing every 30 seconds. Note that an initial delay is used for - // cases like configuration changes, or returning from a saved checkpoint. - lifecycleSafeTimerFactory.run { - activity.runPeriodically(delayMillis = 5_000, periodMillis = 30_000) { - return@runPeriodically viewModel.isHintOpenedAndUnRevealed.get()!!.also { playAnim -> - if (playAnim) binding.hintBulb.startAnimation(hintBulbAnimation) - } + if (!isHintBulbAnimationEnabled.value) return if (isHintUnrevealed) { + val hintBulbAnimation = AnimationUtils.loadAnimation( + context, + R.anim.hint_bulb_animation + ).also { it.interpolator = BounceUpAndDownInterpolator() } + + // The bulb should start bouncing every 30 seconds. Note that an initial delay is used for + // cases like configuration changes, or returning from a saved checkpoint. + lifecycleSafeTimerFactory.run { + activity.runPeriodically(delayMillis = 5_000, periodMillis = 30_000) { + return@runPeriodically viewModel.isHintOpenedAndUnRevealed.get()!!.also { playAnim -> + if (playAnim) binding.hintBulb.startAnimation(hintBulbAnimation) } } - } else { - binding.hintBulb.clearAnimation() } + } else { + binding.hintBulb.clearAnimation() + } } /** 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 7aa8484339a..255c08e05ac 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 @@ -146,7 +146,7 @@ class StatePlayerRecyclerViewAssembler private constructor( private val hasConversationView: Boolean, private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, - private val rawUserAnswer: RawUserAnswer, + private var rawUserAnswer: RawUserAnswer, ) : HtmlParser.CustomOppiaTagActionListener { /** * A list of view models corresponding to past view models that are hidden by default. These are @@ -160,7 +160,7 @@ class StatePlayerRecyclerViewAssembler private constructor( * Whether the previously submitted wrong answers should be expanded. This value is intentionally * not retained upon configuration changes since the user can just re-expand the list. */ - private var hasPreviousResponsesExpanded: Boolean = false + var hasPreviousResponsesExpanded: Boolean = false private val lifecycleSafeTimerFactory = LifecycleSafeTimerFactory(backgroundCoroutineDispatcher) @@ -302,6 +302,13 @@ class StatePlayerRecyclerViewAssembler private constructor( return Pair(conversationPendingItemList, extraInteractionPendingItemList) } + /** + * Resets rawUserAnswer to it's default instance. + */ + fun resetRawUserAnswer() { + rawUserAnswer = RawUserAnswer.getDefaultInstance() + } + private fun addInteractionForPendingState( pendingItemList: MutableList, interaction: Interaction, @@ -315,7 +322,6 @@ class StatePlayerRecyclerViewAssembler private constructor( hasConversationView, rawUserAnswer, interaction, - canSubmitAnswer?.get() ?: false, fragment as InteractionAnswerReceiver, fragment as InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton, 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 54468484c6f..f43189bbc7c 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 @@ -56,7 +56,6 @@ class ContinueInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 0d91cab88c3..3052039393d 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -3,8 +3,6 @@ package org.oppia.android.app.player.state.itemviewmodel import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.recyclerview.widget.RecyclerView -import org.oppia.android.app.model.DragAndDropRawAnswer -import org.oppia.android.app.model.GroupSubtitledHtml import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.ListOfSetsOfHtmlStrings @@ -32,7 +30,6 @@ class DragAndDropSortInteractionViewModel private constructor( val hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, private val writtenTranslationContext: WrittenTranslationContext, @@ -61,17 +58,12 @@ class DragAndDropSortInteractionViewModel private constructor( } private val _choiceItems: MutableList = - if (rawUserAnswer.hasDragAndDrop() && isSubmitAnswerEnabled) computeMergeChoiceItems( + computeChoiceItems( contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler, - rawUserAnswer.dragAndDrop - ) else computeChoiceItems( - contentIdHtmlMap, - choiceSubtitledHtmls, - this, - resourceHandler + rawUserAnswer.listOfSetsOfTranslatableHtmlContentIds ) val choiceItems: List = _choiceItems @@ -146,29 +138,11 @@ class DragAndDropSortInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - val htmlContentIds = _choiceItems.map { it.htmlContent.contentIdsList.toList() } - val htmlContent = _choiceItems.map { it.computeStringList().htmlList.toList() } - val listofSubtitledHtml = htmlContentIds.zip(htmlContent) - val GroupSubtitleHtmlList = mutableListOf() - listofSubtitledHtml.forEach { - val newListofSubtitleHtml = mutableListOf() - it.first.zip(it.second).forEach { - newListofSubtitleHtml.add( - SubtitledHtml.newBuilder().apply { - contentId = it.first.contentId - html = it.second - }.build() - ) - } - GroupSubtitleHtmlList.add( - GroupSubtitledHtml.newBuilder() - .addAllListOfSubtitledHtmls(newListofSubtitleHtml) - .build() - ) - } - dragAndDrop = DragAndDropRawAnswer.newBuilder() - .addAllListOfGroupSubtitledHtml(GroupSubtitleHtmlList) - .build() + val htmlContentIds = _choiceItems.map { it.htmlContent } + listOfSetsOfTranslatableHtmlContentIds = + ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply { + addAllContentIdLists(htmlContentIds) + }.build() }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ @@ -242,7 +216,6 @@ class DragAndDropSortInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, @@ -254,7 +227,6 @@ class DragAndDropSortInteractionViewModel private constructor( hasConversationView, rawUserAnswer, interaction, - isSubmitAnswerEnabled, answerErrorReceiver, isSplitView, writtenTranslationContext, @@ -270,11 +242,24 @@ class DragAndDropSortInteractionViewModel private constructor( choiceStrings: List, dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, resourceHandler: AppLanguageResourceHandler, + listOfSetsOfTranslatableHtmlContentIds: ListOfSetsOfTranslatableHtmlContentIds ): MutableList { - val listOfDragDropInteractionContentViewModel = - mutableListOf() - choiceStrings.mapIndexed { index, subtitledHtml -> - listOfDragDropInteractionContentViewModel.add( + if (listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount > 0) { + return listOfSetsOfTranslatableHtmlContentIds + .contentIdListsList.mapIndexed { itemIndex, setOfTranslatableHtmlContentIds -> + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addAllContentIds(setOfTranslatableHtmlContentIds.contentIdsList) + }.build(), + itemIndex = itemIndex, + listSize = listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() + } else { + return choiceStrings.mapIndexed { index, subtitledHtml -> DragDropInteractionContentViewModel( contentIdHtmlMap = contentIdHtmlMap, htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { @@ -289,60 +274,8 @@ class DragAndDropSortInteractionViewModel private constructor( dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, resourceHandler = resourceHandler ) - ) - } - return listOfDragDropInteractionContentViewModel - } - - private fun computeMergeChoiceItems( - contentIdHtmlMap: Map, - choiceStrings: List, - dragAndDropSortInteractionViewModel: DragAndDropSortInteractionViewModel, - resourceHandler: AppLanguageResourceHandler, - listOfGroupSubtitledHtml: DragAndDropRawAnswer - ): MutableList { - val listOfDragAndDropInteractionContentViewModel = - mutableListOf() - listOfGroupSubtitledHtml.listOfGroupSubtitledHtmlList.forEach { - it.listOfSubtitledHtmlsList.mapIndexed { index, subtitledHtml -> - listOfDragAndDropInteractionContentViewModel.add( - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = subtitledHtml.contentId - } - ) - }.build(), - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - ) - } - } - listOfGroupSubtitledHtml.listOfGroupSubtitledHtmlList.mapIndexed { itemIndex, it -> - if (it.listOfSubtitledHtmlsCount > 1) { - val item = listOfDragAndDropInteractionContentViewModel[itemIndex] - val nextItem = listOfDragAndDropInteractionContentViewModel[itemIndex + 1] - nextItem.htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addAllContentIds(item.htmlContent.contentIdsList) - addAllContentIds(nextItem.htmlContent.contentIdsList) - }.build() - listOfDragAndDropInteractionContentViewModel[itemIndex + 1] = nextItem - - listOfDragAndDropInteractionContentViewModel.removeAt(itemIndex) - - listOfDragAndDropInteractionContentViewModel.forEachIndexed { index, viewModel -> - viewModel.itemIndex = index - viewModel.listSize = - listOfDragAndDropInteractionContentViewModel.size - } - } + }.toMutableList() } - return listOfDragAndDropInteractionContentViewModel } } } 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 b1646ba7f22..1c698e772d3 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,7 +32,7 @@ class FractionInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.FRACTION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = rawUserAnswer.fraction ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") val hintText: CharSequence = deriveHintText(interaction) @@ -88,7 +88,7 @@ class FractionInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - fraction = answerText.toString() + textualAnswer = answerText.toString() } }.build() @@ -146,7 +146,6 @@ class FractionInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 8da66c75c89..9f45ba24820 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,7 +4,7 @@ import androidx.databinding.Observable import androidx.databinding.ObservableField import org.oppia.android.R import org.oppia.android.app.model.ClickOnImage -import org.oppia.android.app.model.ImageWithRegions +import org.oppia.android.app.model.ImageWithRegions.LabeledRegion import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject import org.oppia.android.app.model.RawUserAnswer @@ -34,7 +34,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( InteractionAnswerHandler, OnClickableAreaClickedListener { var answerText: CharSequence = "" - val selectableRegions: List by lazy { + val selectableRegions: List by lazy { val schemaObject = interaction.customizationArgsMap["imageAndRegions"] schemaObject?.customSchemaValue?.imageWithRegions?.labelRegionsList ?: listOf() } @@ -44,8 +44,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( schemaObject?.customSchemaValue?.imageWithRegions?.imagePath ?: "" } val isAnswerAvailable = ObservableField(false) - val lastSelectedRegion = - ObservableField(rawUserAnswer.imageRegionSelection) + val lastSelectedRegion = ObservableField(rawUserAnswer.imageRegionSelection) init { val callback: Observable.OnPropertyChangedCallback = @@ -87,7 +86,9 @@ class ImageRegionSelectionInteractionViewModel private constructor( }.build() override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { - imageRegionSelection = selectableRegions.find { it.label == answerText.toString() } + if (answerText.isNotEmpty()) { + imageRegionSelection = selectableRegions.find { it.label == answerText.toString() } + } }.build() private fun parseClickOnImage(answerTextString: String): ClickOnImage { @@ -108,7 +109,6 @@ class ImageRegionSelectionInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 b2b8347706d..e26b2c3d7ff 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 = rawUserAnswer.mathExpression ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" /** * Defines whether an answer is currently available to parse. This is expected to be directly @@ -146,7 +146,7 @@ class MathExpressionInteractionsViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - mathExpression = answerText.toString() + textualAnswer = answerText.toString() } }.build() @@ -237,7 +237,6 @@ class MathExpressionInteractionsViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 d4fdaf450e8..112928ea665 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,7 +26,7 @@ class NumericInputViewModel private constructor( private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler ) : StateItemViewModel(ViewType.NUMERIC_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer.numeric ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" private var pendingAnswerError: String? = null val errorMessage = ObservableField("") var isAnswerAvailable = ObservableField(false) @@ -96,7 +96,7 @@ class NumericInputViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - numeric = answerText.toString() + textualAnswer = answerText.toString() } }.build() @@ -109,7 +109,6 @@ class NumericInputViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 a9435023a22..62f12344e3a 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,7 +33,7 @@ class RatioExpressionInputInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.RATIO_EXPRESSION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = rawUserAnswer.ratioInput ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") @@ -73,7 +73,7 @@ class RatioExpressionInputInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - ratioInput = answerText.toString() + textualAnswer = answerText.toString() } }.build() @@ -147,7 +147,6 @@ class RatioExpressionInputInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 949e358c62b..c93a74dfa70 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 @@ -31,7 +31,6 @@ class SelectionInteractionViewModel private constructor( val hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length val isSplitView: Boolean, val writtenTranslationContext: WrittenTranslationContext, @@ -75,7 +74,7 @@ class SelectionInteractionViewModel private constructor( ) } } - if (selectedAnswer.size > 0 && isSubmitAnswerEnabled) { + if (selectedAnswer.size > 0) { selectedAnswer.forEach { index -> selectedItems += index updateIsAnswerAvailable() @@ -189,7 +188,6 @@ class SelectionInteractionViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, @@ -201,7 +199,6 @@ class SelectionInteractionViewModel private constructor( hasConversationView, rawUserAnswer, interaction, - isSubmitAnswerEnabled, answerErrorReceiver, isSplitView, writtenTranslationContext, 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 fcde65498c7..034c6d6229e 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 @@ -50,7 +50,6 @@ abstract class StateItemViewModel(val viewType: ViewType) : ObservableViewModel( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 330f5084297..70bdeda7984 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 @@ -28,7 +28,7 @@ class TextInputViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController ) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer.text ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" val hintText: CharSequence = deriveHintText(interaction) var isAnswerAvailable = ObservableField(false) @@ -78,7 +78,7 @@ class TextInputViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { - text = answerText.toString() + textualAnswer = answerText.toString() } }.build() @@ -113,7 +113,6 @@ class TextInputViewModel private constructor( hasConversationView: Boolean, rawUserAnswer: RawUserAnswer, interaction: Interaction, - isSubmitAnswerEnabled: Boolean, interactionAnswerReceiver: InteractionAnswerReceiver, answerErrorReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, hasPreviousButton: Boolean, 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 7ccd689d750..9bd66235064 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 @@ -161,7 +161,6 @@ class InputInteractionViewTestActivity : hasConversationView = false, rawUserAnswer = RawUserAnswer.getDefaultInstance(), interaction = interaction, - isSubmitAnswerEnabled = false, interactionAnswerReceiver = this@InputInteractionViewTestActivity, answerErrorReceiver = this@InputInteractionViewTestActivity, hasPreviousButton = false, 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 8737c084c00..94e1fbcbcd7 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 @@ -240,8 +240,8 @@ class StateFragmentTest { @Before fun setUp() { - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) - TestPlatformParameterModule.forceEnableHintBulbAnimation(false) +// TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) +// TestPlatformParameterModule.forceEnableHintBulbAnimation(false) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() @@ -1629,15 +1629,14 @@ class StateFragmentTest { playThroughPrototypeState4() playThroughPrototypeState5() // Entering text in Numeric Input - typeNumericInput("12a") + typeNumericInput("121") // Rotating device rotateToLandscape() it.onActivity { val numericInputInteractionView = it.findViewById(R.id.numeric_input_interaction_view) - assertThat(numericInputInteractionView.text.toString()).isEqualTo("12a") + assertThat(numericInputInteractionView.text.toString()).isEqualTo("121") } - onView(withId(R.id.number_input_error)).check(matches(isDisplayed())) } } @@ -1711,7 +1710,6 @@ class StateFragmentTest { @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_selectionInteraction_ratioButton_retainStateOnConfigurationChange() { - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() playThroughPrototypeState1() @@ -1734,7 +1732,6 @@ class StateFragmentTest { @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_selectionInteraction_multipleSelection_retainStateOnConfigurationChange() { - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() playThroughPrototypeState1() @@ -1775,7 +1772,6 @@ class StateFragmentTest { @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_dragAndDrop_retainStateOnConfigurationChange() { - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() playThroughPrototypeState1() @@ -1822,6 +1818,56 @@ class StateFragmentTest { } } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_sameTextBasedInteractions_doesNotShareInitialState() { + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + startPlayingExploration() + + typeNumericExpression("1+2") + rotateToLandscape() + clickSubmitAnswerButton() + clickContinueNavigationButton() + + it.onActivity { + val mathExpressionInteractionView = + it.findViewById( + R.id.math_expression_input_interaction_view + ) + assertThat(mathExpressionInteractionView.text.toString().isEmpty()).isTrue() + } + } + } + + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_differentTextBaseInteractions_doesNotShareInitialState() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + clickContinueInteractionButton() + // Entering text in Fraction Input Interaction + typeFractionText("1/2") + // Rotating device + rotateToLandscape() + it.onActivity { + val fractionInputInteraction = + it.findViewById(R.id.fraction_input_interaction_view) + assertThat(fractionInputInteraction.text.toString()).isEqualTo("1/2") + } + clickSubmitAnswerButton() + clickContinueNavigationButton() + playThroughPrototypeState3() + playThroughPrototypeState4() + playThroughPrototypeState5() + + it.onActivity { + val numericInputInteractionView = + it.findViewById(R.id.numeric_input_interaction_view) + assertThat(numericInputInteractionView.text.toString().isEmpty()).isTrue() + } + } + } + @Test fun testStateFragment_ratioInput_textViewHasTextInputType() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { scenario -> diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index 752924aabc3..ab0e42cd882 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -331,37 +331,17 @@ message ItemSelectionRawAnswer { // retain state on configuration changes. message RawUserAnswer { oneof answer_input_type { - // A raw answer entered by user in text input interaction. - string text = 1; - // A raw answer entered by user in fraction input interaction. - string fraction = 2; - // A raw answer entered by user in numeric input interaction. - string numeric = 3; - // A raw answer entered by user in ratio input interaction. - string ratio_input = 4; - // A raw answer entered by user in ratio input interaction. - string math_expression = 5; + // A raw answer entered by user in a text-based interactions. + string textual_answer = 1; // A user's selection for item selection and multiple choice interactions. - ItemSelectionRawAnswer item_selection = 6; - // A user's selection for drag and drop interactions. - DragAndDropRawAnswer drag_and_drop = 7; + ItemSelectionRawAnswer item_selection = 2; // A user's selection for image region selection interaction. - ImageWithRegions.LabeledRegion image_region_selection = 8; + ImageWithRegions.LabeledRegion image_region_selection = 3; + // A user's selected list of set of html content ids in drag and drop interaction. + ListOfSetsOfTranslatableHtmlContentIds list_of_sets_of_translatable_html_content_ids = 4; } } -// Corresponds to a current drag and drop answer -message DragAndDropRawAnswer { - // list of group subtitled html - repeated GroupSubtitledHtml list_of_group_subtitled_html = 1; -} - -// A list of subtitled html -message GroupSubtitledHtml { - // list of subtitled html - repeated SubtitledHtml list_of_subtitled_htmls = 1; -} - message AnswerAndResponse { // A previous answer the learner submitted. UserAnswer user_answer = 1; From 69ef6768e0db598946eb4bb1c1e963faf9b06e44 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 7 Nov 2022 00:59:49 +0530 Subject: [PATCH 39/76] improved code --- .../org/oppia/android/app/player/state/StateFragmentTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 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 94e1fbcbcd7..b41a38a5479 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 @@ -240,8 +240,8 @@ class StateFragmentTest { @Before fun setUp() { -// TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) -// TestPlatformParameterModule.forceEnableHintBulbAnimation(false) + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) + TestPlatformParameterModule.forceEnableHintBulbAnimation(false) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() From bb77a939887256b3f5989a8b62fc027c04995e94 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 7 Nov 2022 11:08:13 +0530 Subject: [PATCH 40/76] Nit changes and added KDoC --- .../state/ImageRegionSelectionInteractionView.kt | 2 +- .../android/app/player/state/StateFragment.kt | 15 ++++++++------- .../app/player/state/StateFragmentPresenter.kt | 6 +++--- .../android/app/player/state/StateViewModel.kt | 6 ++++++ .../state/itemviewmodel/TextInputViewModel.kt | 1 - .../questionplayer/QuestionPlayerFragment.kt | 11 ++++++++++- .../QuestionPlayerFragmentPresenter.kt | 8 ++++++++ 7 files changed, 36 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index c944910d5ab..7db1d78f0bf 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -126,7 +126,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( clickableAreas ) areasImage.addRegionViews() - if (lastSelectedRegion.hasRegion()) { + if (::lastSelectedRegion.isInitialized && lastSelectedRegion.hasRegion()) { areasImage.showOrHideRegion(lastSelectedRegion) } performAttachment(areasImage) 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 04c014f9a44..2ced62e9f21 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 @@ -26,8 +26,8 @@ import org.oppia.android.util.extensions.putProto import javax.inject.Inject private const val STATE_FRAGMENT_RAW_USER_ANSWER_KEY = "StateFragment.raw_user_answer" -private const val STATE_FRAGMENT_HAS_PREVIOUS_HEADER_COLLAPSED = - "StateFragment.has_response_header_collapsed" +private const val STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = + "StateFragment.is_previous_responses_expanded_collapsed" /** Fragment that represents the current state of an exploration. */ class StateFragment : @@ -89,8 +89,9 @@ class StateFragment : val rawUserAnswer = savedInstanceState?.getProto( STATE_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() ) ?: RawUserAnswer.getDefaultInstance() - val hasPreviousResponsesExpanded = - savedInstanceState?.getBoolean(STATE_FRAGMENT_HAS_PREVIOUS_HEADER_COLLAPSED) ?: false + val isPreviousResponsesExpanded = + savedInstanceState?.getBoolean(STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY) + ?: false return stateFragmentPresenter.handleCreateView( inflater, container, @@ -98,7 +99,7 @@ class StateFragment : topicId, storyId, rawUserAnswer, - hasPreviousResponsesExpanded, + isPreviousResponsesExpanded, explorationId ) } @@ -150,8 +151,8 @@ class StateFragment : stateFragmentPresenter.getRawUserAnswer() ) outState.putBoolean( - STATE_FRAGMENT_HAS_PREVIOUS_HEADER_COLLAPSED, - stateFragmentPresenter.getHasPreviousResponsesExpanded() + STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, + stateFragmentPresenter.getIsPreviousResponsesExpanded() ) } 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 1707ef4bb63..91d3f173634 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 @@ -114,7 +114,7 @@ class StateFragmentPresenter @Inject constructor( topicId: String, storyId: String, rawUserAnswer: RawUserAnswer, - hasPreviousResponsesExpanded: Boolean, + isPreviousResponsesExpanded: Boolean, explorationId: String ): View? { profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() @@ -134,7 +134,7 @@ class StateFragmentPresenter @Inject constructor( binding.fullScreenConfettiView ) - recyclerViewAssembler.hasPreviousResponsesExpanded = hasPreviousResponsesExpanded + recyclerViewAssembler.hasPreviousResponsesExpanded = isPreviousResponsesExpanded val stateRecyclerViewAdapter = recyclerViewAssembler.adapter val rhsStateRecyclerViewAdapter = recyclerViewAssembler.rhsAdapter @@ -274,7 +274,7 @@ class StateFragmentPresenter @Inject constructor( subscribeToHintSolution(explorationProgressController.submitSolutionIsRevealed()) } - fun getHasPreviousResponsesExpanded(): Boolean { + fun getIsPreviousResponsesExpanded(): Boolean { return recyclerViewAssembler.hasPreviousResponsesExpanded } 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 a748a134ba5..e2946f0fb2b 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 @@ -57,6 +57,12 @@ class StateViewModel @Inject constructor() : ObservableViewModel() { ) ?: UserAnswer.getDefaultInstance() } + /** + * Retrieves rawUserAnswer from given Interaction Answer Handler. + * + * @param retrieveAnswerHandler list of [StateItemViewModel]. + * @return a RawUserAnswer from given [retrieveAnswerHandler]. + */ fun getRawUserAnswer( retrieveAnswerHandler: (List) -> InteractionAnswerHandler? ): RawUserAnswer { 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 70bdeda7984..fb8e3da8bbd 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 @@ -43,7 +43,6 @@ class TextInputViewModel private constructor( ) } } - isAnswerAvailable.addOnPropertyChangedCallback(callback) } 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 12e498ce675..e6ec2dbfc50 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 @@ -26,6 +26,8 @@ import javax.inject.Inject private const val QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY = "QuestionPlayerFragment.raw_user_answer" +private const val STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = + "StateFragment.is_previous_responses_expanded_collapsed" /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : @@ -59,9 +61,12 @@ class QuestionPlayerFragment : val rawUserAnswer = savedInstanceState?.getProto( QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() ) ?: RawUserAnswer.getDefaultInstance() + val isPreviousResponsesExpanded = + savedInstanceState?.getBoolean(STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY) + ?: false val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) return questionPlayerFragmentPresenter.handleCreateView( - inflater, container, rawUserAnswer, profileId + inflater, container, rawUserAnswer, isPreviousResponsesExpanded, profileId ) } @@ -98,6 +103,10 @@ class QuestionPlayerFragment : QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY, questionPlayerFragmentPresenter.getRawUserAnswer() ) + outState.putBoolean( + STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, + questionPlayerFragmentPresenter.getIsPreviousResponsesExpanded() + ) } fun handleKeyboardAction() = questionPlayerFragmentPresenter.handleKeyboardAction() 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 c1c44411c75..0e94a5c4c03 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 @@ -74,6 +74,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( inflater: LayoutInflater, container: ViewGroup?, rawUserAnswer: RawUserAnswer, + isPreviousResponsesExpanded: Boolean, profileId: ProfileId ): View? { binding = QuestionPlayerFragmentBinding.inflate( @@ -88,6 +89,8 @@ class QuestionPlayerFragmentPresenter @Inject constructor( binding.congratulationsTextConfettiView, ) + recyclerViewAssembler.hasPreviousResponsesExpanded = isPreviousResponsesExpanded + binding.apply { lifecycleOwner = fragment viewModel = questionViewModel @@ -117,6 +120,10 @@ class QuestionPlayerFragmentPresenter @Inject constructor( subscribeToHintSolution(questionAssessmentProgressController.submitSolutionIsRevealed()) } + fun getIsPreviousResponsesExpanded(): Boolean { + return recyclerViewAssembler.hasPreviousResponsesExpanded + } + fun dismissConceptCard() { fragment.childFragmentManager.findFragmentByTag( CONCEPT_CARD_DIALOG_FRAGMENT_TAG @@ -149,6 +156,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } fun onSubmitButtonClicked() { + recyclerViewAssembler.resetRawUserAnswer() hideKeyboard() handleSubmitAnswer( questionViewModel.getPendingAnswer( From b0da1736cbacb709cdcfe0985f829266600c3505 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 7 Nov 2022 18:11:05 +0530 Subject: [PATCH 41/76] Added new test to check responseHeader --- .../app/player/state/StateFragmentTest.kt | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) 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 b41a38a5479..439366a7d11 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 @@ -7,8 +7,10 @@ import android.text.Spanned import android.text.style.ClickableSpan import android.text.style.ImageSpan import android.view.View +import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.children import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario.launch @@ -17,6 +19,7 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.PerformException import androidx.test.espresso.UiController import androidx.test.espresso.ViewAction +import androidx.test.espresso.ViewAssertion import androidx.test.espresso.ViewInteraction import androidx.test.espresso.action.GeneralLocation import androidx.test.espresso.action.Press @@ -96,6 +99,7 @@ import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewT import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.NEXT_NAVIGATION_BUTTON import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.NUMERIC_EXPRESSION_INPUT_INTERACTION import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.NUMERIC_INPUT_INTERACTION +import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.PREVIOUS_RESPONSES_HEADER import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.RATIO_EXPRESSION_INPUT_INTERACTION import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.RETURN_TO_TOPIC_NAVIGATION_BUTTON import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.SELECTION_INTERACTION @@ -1868,6 +1872,53 @@ class StateFragmentTest { } } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_checkPreviousHeader_retainStateOnConfigChange() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + clickContinueInteractionButton() + + // Attempt to submit an wrong answer. + typeFractionText("1/4") + clickSubmitAnswerButton() + + // Attempt to submit an wrong answer. + typeFractionText("1/4") + clickSubmitAnswerButton() + + // Expand previous response header. + scrollToViewType(PREVIOUS_RESPONSES_HEADER) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.previous_response_header)).perform(click()) + testCoroutineDispatchers.runCurrent() + + // rotate screen + rotateToLandscape() + + // Both failed answers should be showing. + onView(withId(R.id.state_recycler_view)) + .check( + matchesChildren(matcher = withId(R.id.submitted_answer_container), times = 2) + ) + + // rotate screen + rotateToLandscape() + + // Collapse previous response header. + scrollToViewType(PREVIOUS_RESPONSES_HEADER) + testCoroutineDispatchers.runCurrent() + onView(withId(R.id.previous_response_header)).perform(click()) + testCoroutineDispatchers.runCurrent() + + // Only the latest failed answer should be showing. + onView(withId(R.id.state_recycler_view)) + .check( + matchesChildren(matcher = withId(R.id.submitted_answer_container), times = 1) + ) + } + } + @Test fun testStateFragment_ratioInput_textViewHasTextInputType() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { scenario -> @@ -4554,6 +4605,36 @@ class StateFragmentTest { return find { text in it.first }?.second } + private fun matchesChildren(matcher: Matcher, times: Int): ViewAssertion { + return matches( + object : TypeSafeMatcher() { + override fun describeTo(description: Description?) { + description + ?.appendDescriptionOf(matcher) + ?.appendText(" occurs times: $times in child views") + } + + override fun matchesSafely(view: View?): Boolean { + if (view !is ViewGroup) { + throw PerformException.Builder() + .withCause(IllegalStateException("Expected to match against view group, not: $view")) + .build() + } + val matchingCount = view.children.filter(matcher::matches).count() + if (matchingCount != times) { + throw PerformException.Builder() + .withActionDescription("Expected to match $matcher against $times children") + .withViewDescription("$view") + .withCause( + IllegalStateException("Matched $matchingCount times in $view (expected $times)") + ) + .build() + } + return true + } + }) + } + @Module class TestModule { @Provides From 47d8a4421d0d38193a56eb5c7737109b94dd3157 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 7 Nov 2022 18:21:28 +0530 Subject: [PATCH 42/76] optimized code --- .../itemviewmodel/DragAndDropSortInteractionViewModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index 3052039393d..5d5067a1eca 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 @@ -244,8 +244,8 @@ class DragAndDropSortInteractionViewModel private constructor( resourceHandler: AppLanguageResourceHandler, listOfSetsOfTranslatableHtmlContentIds: ListOfSetsOfTranslatableHtmlContentIds ): MutableList { - if (listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount > 0) { - return listOfSetsOfTranslatableHtmlContentIds + return if (listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount > 0) { + listOfSetsOfTranslatableHtmlContentIds .contentIdListsList.mapIndexed { itemIndex, setOfTranslatableHtmlContentIds -> DragDropInteractionContentViewModel( contentIdHtmlMap = contentIdHtmlMap, @@ -259,7 +259,7 @@ class DragAndDropSortInteractionViewModel private constructor( ) }.toMutableList() } else { - return choiceStrings.mapIndexed { index, subtitledHtml -> + choiceStrings.mapIndexed { index, subtitledHtml -> DragDropInteractionContentViewModel( contentIdHtmlMap = contentIdHtmlMap, htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { From 06a0b3dcd15d02d0bc4a0d4d4cad32867e8d478c Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 7 Nov 2022 23:23:27 +0530 Subject: [PATCH 43/76] Nit changes --- .../oppia/android/app/player/state/StateFragment.kt | 2 +- .../android/app/player/state/StateFragmentPresenter.kt | 4 ++-- .../player/state/StatePlayerRecyclerViewAssembler.kt | 10 +++++----- .../app/topic/questionplayer/QuestionPlayerFragment.kt | 8 ++++---- .../questionplayer/QuestionPlayerFragmentPresenter.kt | 4 ++-- 5 files changed, 14 insertions(+), 14 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 2ced62e9f21..385ea8c9728 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 @@ -27,7 +27,7 @@ import javax.inject.Inject private const val STATE_FRAGMENT_RAW_USER_ANSWER_KEY = "StateFragment.raw_user_answer" private const val STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = - "StateFragment.is_previous_responses_expanded_collapsed" + "StateFragment.is_previous_responses_header_expanded" /** 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 91d3f173634..c5deb82c941 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 @@ -134,7 +134,7 @@ class StateFragmentPresenter @Inject constructor( binding.fullScreenConfettiView ) - recyclerViewAssembler.hasPreviousResponsesExpanded = isPreviousResponsesExpanded + recyclerViewAssembler.isPreviousResponsesExpanded = isPreviousResponsesExpanded val stateRecyclerViewAdapter = recyclerViewAssembler.adapter val rhsStateRecyclerViewAdapter = recyclerViewAssembler.rhsAdapter @@ -275,7 +275,7 @@ class StateFragmentPresenter @Inject constructor( } fun getIsPreviousResponsesExpanded(): Boolean { - return recyclerViewAssembler.hasPreviousResponsesExpanded + return recyclerViewAssembler.isPreviousResponsesExpanded } private fun getStateViewModel(): StateViewModel { 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 255c08e05ac..d64b1afde4f 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 @@ -160,7 +160,7 @@ class StatePlayerRecyclerViewAssembler private constructor( * Whether the previously submitted wrong answers should be expanded. This value is intentionally * not retained upon configuration changes since the user can just re-expand the list. */ - var hasPreviousResponsesExpanded: Boolean = false + var isPreviousResponsesExpanded: Boolean = false private val lifecycleSafeTimerFactory = LifecycleSafeTimerFactory(backgroundCoroutineDispatcher) @@ -363,7 +363,7 @@ class StatePlayerRecyclerViewAssembler private constructor( PreviousResponsesHeaderViewModel( answersAndResponses.size - 1, hasConversationView, - ObservableBoolean(hasPreviousResponsesExpanded), + ObservableBoolean(isPreviousResponsesExpanded), fragment as PreviousResponsesHeaderClickListener, isSplitView.get()!!, resourceHandler @@ -374,7 +374,7 @@ class StatePlayerRecyclerViewAssembler private constructor( } // Only add previous answers if current responses are expanded, or if collapsing is disabled. val showPreviousAnswers = !playerFeatureSet.wrongAnswerCollapsing || - hasPreviousResponsesExpanded + isPreviousResponsesExpanded for (answerAndResponse in answersAndResponses.take(answersAndResponses.size - 1)) { if (playerFeatureSet.pastAnswerSupport) { // Earlier answers can't be correct (since otherwise new answers wouldn't be able to be @@ -452,7 +452,7 @@ class StatePlayerRecyclerViewAssembler private constructor( } // Ensure the header matches the updated state. headerModel.isExpanded.set(expandPreviousAnswers) - hasPreviousResponsesExpanded = expandPreviousAnswers + isPreviousResponsesExpanded = expandPreviousAnswers } /** @@ -464,7 +464,7 @@ class StatePlayerRecyclerViewAssembler private constructor( check(playerFeatureSet.wrongAnswerCollapsing) { "Cannot collapse previous answers for assembler that doesn't support wrong answer collapsing" } - hasPreviousResponsesExpanded = false + isPreviousResponsesExpanded = false } /** 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 e6ec2dbfc50..b1d3aaa99a1 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 @@ -26,8 +26,8 @@ import javax.inject.Inject private const val QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY = "QuestionPlayerFragment.raw_user_answer" -private const val STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = - "StateFragment.is_previous_responses_expanded_collapsed" +private const val QUESTION_PLAYER_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = + "QuestionPlayerFragment.is_previous_responses_header_expanded" /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : @@ -62,7 +62,7 @@ class QuestionPlayerFragment : QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() ) ?: RawUserAnswer.getDefaultInstance() val isPreviousResponsesExpanded = - savedInstanceState?.getBoolean(STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY) + savedInstanceState?.getBoolean(QUESTION_PLAYER_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY) ?: false val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) return questionPlayerFragmentPresenter.handleCreateView( @@ -104,7 +104,7 @@ class QuestionPlayerFragment : questionPlayerFragmentPresenter.getRawUserAnswer() ) outState.putBoolean( - STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, + QUESTION_PLAYER_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, questionPlayerFragmentPresenter.getIsPreviousResponsesExpanded() ) } 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 0e94a5c4c03..63556102bd1 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 @@ -89,7 +89,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( binding.congratulationsTextConfettiView, ) - recyclerViewAssembler.hasPreviousResponsesExpanded = isPreviousResponsesExpanded + recyclerViewAssembler.isPreviousResponsesExpanded = isPreviousResponsesExpanded binding.apply { lifecycleOwner = fragment @@ -121,7 +121,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } fun getIsPreviousResponsesExpanded(): Boolean { - return recyclerViewAssembler.hasPreviousResponsesExpanded + return recyclerViewAssembler.isPreviousResponsesExpanded } fun dismissConceptCard() { From 0529d7921c525ad1d38637ca3d04db8af3533769 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 7 Nov 2022 23:24:11 +0530 Subject: [PATCH 44/76] Code optimized --- .../app/topic/questionplayer/QuestionPlayerFragment.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 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 b1d3aaa99a1..ffbbab3ad57 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 @@ -62,8 +62,9 @@ class QuestionPlayerFragment : QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() ) ?: RawUserAnswer.getDefaultInstance() val isPreviousResponsesExpanded = - savedInstanceState?.getBoolean(QUESTION_PLAYER_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY) - ?: false + savedInstanceState?.getBoolean( + QUESTION_PLAYER_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY + ) ?: false val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) return questionPlayerFragmentPresenter.handleCreateView( inflater, container, rawUserAnswer, isPreviousResponsesExpanded, profileId From bb940423a192a86a989503963427c37cc54bed3c Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 8 Nov 2022 18:06:29 +0530 Subject: [PATCH 45/76] Updated tests and nit changes --- .../ImageRegionSelectionInteractionView.kt | 2 +- .../android/app/player/state/StateFragment.kt | 10 ++-- .../player/state/StateFragmentPresenter.kt | 18 ++++--- .../state/StatePlayerRecyclerViewAssembler.kt | 29 +++++++---- .../app/player/state/StateViewModel.kt | 7 +-- .../DragAndDropSortInteractionViewModel.kt | 50 ++++++++---------- .../SelectionInteractionViewModel.kt | 6 +-- .../questionplayer/QuestionPlayerFragment.kt | 8 +-- .../QuestionPlayerFragmentPresenter.kt | 14 +++-- .../questionplayer/QuestionPlayerViewModel.kt | 1 + .../app/utility/ClickableAreasImage.kt | 11 ++-- .../app/player/state/StateFragmentTest.kt | 51 ++++++++++++++----- 12 files changed, 118 insertions(+), 89 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index 7db1d78f0bf..c1a3f3381df 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -127,7 +127,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( ) areasImage.addRegionViews() if (::lastSelectedRegion.isInitialized && lastSelectedRegion.hasRegion()) { - areasImage.showOrHideRegion(lastSelectedRegion) + areasImage.toggleRegion(lastSelectedRegion) } performAttachment(areasImage) } 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 385ea8c9728..ce80bdc3f53 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 @@ -26,8 +26,8 @@ import org.oppia.android.util.extensions.putProto import javax.inject.Inject private const val STATE_FRAGMENT_RAW_USER_ANSWER_KEY = "StateFragment.raw_user_answer" -private const val STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = - "StateFragment.is_previous_responses_header_expanded" +private const val STATE_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = + "StateFragment.are_previous_responses_header_expanded" /** Fragment that represents the current state of an exploration. */ class StateFragment : @@ -90,7 +90,7 @@ class StateFragment : STATE_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() ) ?: RawUserAnswer.getDefaultInstance() val isPreviousResponsesExpanded = - savedInstanceState?.getBoolean(STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY) + savedInstanceState?.getBoolean(STATE_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY) ?: false return stateFragmentPresenter.handleCreateView( inflater, @@ -151,8 +151,8 @@ class StateFragment : stateFragmentPresenter.getRawUserAnswer() ) outState.putBoolean( - STATE_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, - stateFragmentPresenter.getIsPreviousResponsesExpanded() + STATE_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, + stateFragmentPresenter.getArePreviousResponsesExpanded() ) } 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 c5deb82c941..4ae7993be27 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 @@ -114,7 +114,7 @@ class StateFragmentPresenter @Inject constructor( topicId: String, storyId: String, rawUserAnswer: RawUserAnswer, - isPreviousResponsesExpanded: Boolean, + arePreviousResponsesExpanded: Boolean, explorationId: String ): View? { profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build() @@ -128,14 +128,18 @@ class StateFragmentPresenter @Inject constructor( /* attachToRoot= */ false ) recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, entityType, profileId, rawUserAnswer), + assemblerBuilderFactory.create( + resourceBucketName, + entityType, + profileId, + rawUserAnswer, + arePreviousResponsesExpanded + ), binding.congratulationsTextView, binding.congratulationsTextConfettiView, binding.fullScreenConfettiView ) - recyclerViewAssembler.isPreviousResponsesExpanded = isPreviousResponsesExpanded - val stateRecyclerViewAdapter = recyclerViewAssembler.adapter val rhsStateRecyclerViewAdapter = recyclerViewAssembler.rhsAdapter binding.stateRecyclerView.apply { @@ -274,8 +278,9 @@ class StateFragmentPresenter @Inject constructor( subscribeToHintSolution(explorationProgressController.submitSolutionIsRevealed()) } - fun getIsPreviousResponsesExpanded(): Boolean { - return recyclerViewAssembler.isPreviousResponsesExpanded + /** Returns whether previously submitted wrong answers should be expanded or not. */ + fun getArePreviousResponsesExpanded(): Boolean { + return recyclerViewAssembler.arePreviousResponsesExpanded } private fun getStateViewModel(): StateViewModel { @@ -468,6 +473,7 @@ class StateFragmentPresenter @Inject constructor( /** Returns the checkpoint state for the current exploration. */ fun getExplorationCheckpointState() = explorationCheckpointState + /** Returns [RawUserAnswer] from stateViewModel's [getRawUserAnswer]. */ fun getRawUserAnswer(): RawUserAnswer { return if (isConfigChangeStateRetentionEnabled.value) { viewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) 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 d64b1afde4f..984630109aa 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 @@ -147,6 +147,7 @@ class StatePlayerRecyclerViewAssembler private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController, private var rawUserAnswer: RawUserAnswer, + arePreviousResponsesExpanded: Boolean ) : HtmlParser.CustomOppiaTagActionListener { /** * A list of view models corresponding to past view models that are hidden by default. These are @@ -160,7 +161,8 @@ class StatePlayerRecyclerViewAssembler private constructor( * Whether the previously submitted wrong answers should be expanded. This value is intentionally * not retained upon configuration changes since the user can just re-expand the list. */ - var isPreviousResponsesExpanded: Boolean = false + var arePreviousResponsesExpanded: Boolean = arePreviousResponsesExpanded + private set private val lifecycleSafeTimerFactory = LifecycleSafeTimerFactory(backgroundCoroutineDispatcher) @@ -363,7 +365,7 @@ class StatePlayerRecyclerViewAssembler private constructor( PreviousResponsesHeaderViewModel( answersAndResponses.size - 1, hasConversationView, - ObservableBoolean(isPreviousResponsesExpanded), + ObservableBoolean(arePreviousResponsesExpanded), fragment as PreviousResponsesHeaderClickListener, isSplitView.get()!!, resourceHandler @@ -374,7 +376,7 @@ class StatePlayerRecyclerViewAssembler private constructor( } // Only add previous answers if current responses are expanded, or if collapsing is disabled. val showPreviousAnswers = !playerFeatureSet.wrongAnswerCollapsing || - isPreviousResponsesExpanded + arePreviousResponsesExpanded for (answerAndResponse in answersAndResponses.take(answersAndResponses.size - 1)) { if (playerFeatureSet.pastAnswerSupport) { // Earlier answers can't be correct (since otherwise new answers wouldn't be able to be @@ -452,7 +454,7 @@ class StatePlayerRecyclerViewAssembler private constructor( } // Ensure the header matches the updated state. headerModel.isExpanded.set(expandPreviousAnswers) - isPreviousResponsesExpanded = expandPreviousAnswers + arePreviousResponsesExpanded = expandPreviousAnswers } /** @@ -464,7 +466,7 @@ class StatePlayerRecyclerViewAssembler private constructor( check(playerFeatureSet.wrongAnswerCollapsing) { "Cannot collapse previous answers for assembler that doesn't support wrong answer collapsing" } - isPreviousResponsesExpanded = false + arePreviousResponsesExpanded = false } /** @@ -900,7 +902,8 @@ class StatePlayerRecyclerViewAssembler private constructor( private val translationController: TranslationController, private val multiTypeBuilderFactory: BindableAdapter.MultiTypeBuilder.Factory, private val singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory, - private val rawUserAnswer: RawUserAnswer + private val rawUserAnswer: RawUserAnswer, + private val arePreviousResponsesExpanded: Boolean ) { private val adapterBuilder: BindableAdapter.MultiTypeBuilder) -> InteractionAnswerHandler? ): RawUserAnswer { 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 5d5067a1eca..f19eeff4799 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 @@ -244,38 +244,28 @@ class DragAndDropSortInteractionViewModel private constructor( resourceHandler: AppLanguageResourceHandler, listOfSetsOfTranslatableHtmlContentIds: ListOfSetsOfTranslatableHtmlContentIds ): MutableList { - return if (listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount > 0) { - listOfSetsOfTranslatableHtmlContentIds - .contentIdListsList.mapIndexed { itemIndex, setOfTranslatableHtmlContentIds -> - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { - addAllContentIds(setOfTranslatableHtmlContentIds.contentIdsList) - }.build(), - itemIndex = itemIndex, - listSize = listOfSetsOfTranslatableHtmlContentIds.contentIdListsCount, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() - } else { - choiceStrings.mapIndexed { index, subtitledHtml -> - DragDropInteractionContentViewModel( - contentIdHtmlMap = contentIdHtmlMap, - htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + val selectedChoices = + if (listOfSetsOfTranslatableHtmlContentIds.contentIdListsList.isEmpty()) { + choiceStrings.map { subtitledHtml -> + SetOfTranslatableHtmlContentIds.newBuilder().apply { addContentIds( - TranslatableHtmlContentId.newBuilder().apply { - contentId = subtitledHtml.contentId - } + TranslatableHtmlContentId.newBuilder().setContentId(subtitledHtml.contentId) ) - }.build(), - itemIndex = index, - listSize = choiceStrings.size, - dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, - resourceHandler = resourceHandler - ) - }.toMutableList() - } + } + } + } else listOfSetsOfTranslatableHtmlContentIds.contentIdListsList + return selectedChoices.mapIndexed { index, contentId -> + DragDropInteractionContentViewModel( + contentIdHtmlMap = contentIdHtmlMap, + htmlContent = SetOfTranslatableHtmlContentIds.newBuilder().apply { + addAllContentIds(contentId.contentIdsList) + }.build(), + itemIndex = index, + listSize = choiceStrings.size, + dragAndDropSortInteractionViewModel = dragAndDropSortInteractionViewModel, + resourceHandler = resourceHandler + ) + }.toMutableList() } } } 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 c93a74dfa70..ed026a3fd74 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 @@ -56,8 +56,6 @@ class SelectionInteractionViewModel private constructor( } private val selectedItems: MutableList = mutableListOf() - private val selectedAnswer: MutableList = - rawUserAnswer.itemSelection.selectedIndexesList ?: mutableListOf() val choiceItems: ObservableList = computeChoiceItems(choiceSubtitledHtmls, hasConversationView, this) @@ -74,8 +72,8 @@ class SelectionInteractionViewModel private constructor( ) } } - if (selectedAnswer.size > 0) { - selectedAnswer.forEach { index -> + if (rawUserAnswer.itemSelection.selectedIndexesList.isNotEmpty()) { + rawUserAnswer.itemSelection.selectedIndexesList.forEach { index -> selectedItems += index updateIsAnswerAvailable() choiceItems[index].isAnswerSelected.set(true) 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 ffbbab3ad57..037c8f9929d 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 @@ -26,8 +26,8 @@ import javax.inject.Inject private const val QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY = "QuestionPlayerFragment.raw_user_answer" -private const val QUESTION_PLAYER_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = - "QuestionPlayerFragment.is_previous_responses_header_expanded" +private const val QUESTION_PLAYER_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY = + "QuestionPlayerFragment.are_previous_responses_header_expanded" /** Fragment that contains all questions in Question Player. */ class QuestionPlayerFragment : @@ -63,7 +63,7 @@ class QuestionPlayerFragment : ) ?: RawUserAnswer.getDefaultInstance() val isPreviousResponsesExpanded = savedInstanceState?.getBoolean( - QUESTION_PLAYER_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY + QUESTION_PLAYER_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY ) ?: false val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) return questionPlayerFragmentPresenter.handleCreateView( @@ -105,7 +105,7 @@ class QuestionPlayerFragment : questionPlayerFragmentPresenter.getRawUserAnswer() ) outState.putBoolean( - QUESTION_PLAYER_FRAGMENT_IS_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, + QUESTION_PLAYER_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, questionPlayerFragmentPresenter.getIsPreviousResponsesExpanded() ) } 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 63556102bd1..24e4cab9faa 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 @@ -84,13 +84,17 @@ class QuestionPlayerFragmentPresenter @Inject constructor( ) recyclerViewAssembler = createRecyclerViewAssembler( - assemblerBuilderFactory.create(resourceBucketName, "skill", profileId, rawUserAnswer), + assemblerBuilderFactory.create( + resourceBucketName, + "skill", + profileId, + rawUserAnswer, + isPreviousResponsesExpanded + ), binding.congratulationsTextView, binding.congratulationsTextConfettiView, ) - recyclerViewAssembler.isPreviousResponsesExpanded = isPreviousResponsesExpanded - binding.apply { lifecycleOwner = fragment viewModel = questionViewModel @@ -120,8 +124,9 @@ class QuestionPlayerFragmentPresenter @Inject constructor( subscribeToHintSolution(questionAssessmentProgressController.submitSolutionIsRevealed()) } + /** Returns whether previously submitted wrong answers should be expanded or not. */ fun getIsPreviousResponsesExpanded(): Boolean { - return recyclerViewAssembler.isPreviousResponsesExpanded + return recyclerViewAssembler.arePreviousResponsesExpanded } fun dismissConceptCard() { @@ -404,6 +409,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } } + /** Returns [RawUserAnswer] from stateViewModel's [getRawUserAnswer]. */ fun getRawUserAnswer(): RawUserAnswer { return if (isConfigChangeStateRetentionEnabled.value) { questionViewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) 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 162c34b89cc..150a7c32659 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 @@ -103,6 +103,7 @@ class QuestionPlayerViewModel @Inject constructor( } } + /** Retrieves the [RawUserAnswer] from the handler provided by [retrieveAnswerHandler]. */ fun getRawUserAnswer( retrieveAnswerHandler: (List) -> InteractionAnswerHandler? ): RawUserAnswer { diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index 5c6166c3c05..9aca0d44f27 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -90,7 +90,7 @@ class ClickableAreasImage( val newView = createSelectableView(clickableArea) newView.setOnTouchListener { _, event -> if (event.action == MotionEvent.ACTION_DOWN) { - showOrHideRegion(clickableArea) + toggleRegion(clickableArea) } return@setOnTouchListener true } @@ -98,7 +98,7 @@ class ClickableAreasImage( // Make default region visibility gone when talkback enabled to avoid any accidental touch. defaultRegionView.isVisible = false newView.setOnClickListener { - showOrHideRegion(clickableArea) + toggleRegion(clickableArea) } } } @@ -119,11 +119,10 @@ class ClickableAreasImage( } /** - * Called to show or hide selected image region - * - * @param clickableArea a clickable image region which we want to show or hide + * Toggles whether the clickable region corresponding to the provided [clickableArea] is visible + * and available to be clicked. */ - fun showOrHideRegion(clickableArea: ImageWithRegions.LabeledRegion) { + fun toggleRegion(clickableArea: ImageWithRegions.LabeledRegion) { resetRegionSelectionViews() listener.onClickableAreaTouched( NamedRegionClickedEvent( 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 439366a7d11..fae7faf8e29 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 @@ -59,6 +59,7 @@ import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.not import org.hamcrest.Description import org.hamcrest.Matcher +import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.TypeSafeMatcher import org.junit.After import org.junit.Before @@ -197,6 +198,8 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton +import junit.framework.Assert.assertTrue +import kotlinx.android.synthetic.main.drag_drop_single_item.view.* /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -244,8 +247,8 @@ class StateFragmentTest { @Before fun setUp() { - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) - TestPlatformParameterModule.forceEnableHintBulbAnimation(false) +// TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) +// TestPlatformParameterModule.forceEnableHintBulbAnimation(false) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() @@ -1803,11 +1806,12 @@ class StateFragmentTest { @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) - fun testStateFragment_dragAndDropSort_retainStateOnConfigurationChange() { + fun testStateFragment_dragAndDrop_mergeFirstTwoItems_dragItem_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_4, shouldSavePartialProgress = false).use { startPlayingExploration() mergeDragAndDropItems(position = 0) + dragAndDropItem(fromPosition = 0, toPosition = 2) // Rotating device. rotateToLandscape() @@ -1815,10 +1819,20 @@ class StateFragmentTest { onView( atPositionOnView( recyclerViewId = R.id.drag_drop_interaction_recycler_view, - position = 0, + position = 2, targetViewId = R.id.drag_drop_item_recyclerview ) ).check(matches(hasChildCount(2))) + onView( + atPositionOnView( + recyclerViewId = R.id.drag_drop_interaction_recycler_view, + position = 2, + targetViewId = R.id.drag_drop_item_recyclerview + ) + ).check{ view, _ -> + val textView = view.findViewById(R.id.drag_drop_content_text_view) + assertThat(textView.text.toString(), containsString("a camera at the store")) + } } } @@ -1874,7 +1888,7 @@ class StateFragmentTest { @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) - fun testStateFragment_checkPreviousHeader_retainStateOnConfigChange() { + fun testStateFragment_checkPreviousHeaderExpanded_retainStateOnConfigChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() clickContinueInteractionButton() @@ -1883,7 +1897,7 @@ class StateFragmentTest { typeFractionText("1/4") clickSubmitAnswerButton() - // Attempt to submit an wrong answer. + // Attempt to submit a second wrong answer. typeFractionText("1/4") clickSubmitAnswerButton() @@ -1901,15 +1915,26 @@ class StateFragmentTest { .check( matchesChildren(matcher = withId(R.id.submitted_answer_container), times = 2) ) + } + } - // rotate screen - rotateToLandscape() + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_checkPreviousHeaderCollapsed_retainStateOnConfigChange() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + clickContinueInteractionButton() - // Collapse previous response header. - scrollToViewType(PREVIOUS_RESPONSES_HEADER) - testCoroutineDispatchers.runCurrent() - onView(withId(R.id.previous_response_header)).perform(click()) - testCoroutineDispatchers.runCurrent() + // Attempt to submit an wrong answer. + typeFractionText("1/4") + clickSubmitAnswerButton() + + // Attempt to submit a second wrong answer. + typeFractionText("1/4") + clickSubmitAnswerButton() + + // rotate screen (by default responses are collapsed) + rotateToLandscape() // Only the latest failed answer should be showing. onView(withId(R.id.state_recycler_view)) From 5c227fd37188be8a8c36a120fabb79de7e8b91b9 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Tue, 8 Nov 2022 18:22:23 +0530 Subject: [PATCH 46/76] Added KDocs --- .../android/app/player/state/StateFragmentPresenter.kt | 4 +++- .../state/answerhandling/InteractionAnswerHandler.kt | 10 ++++++++-- .../android/app/player/state/StateFragmentTest.kt | 9 ++++----- 3 files changed, 15 insertions(+), 8 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 4ae7993be27..dba2dc2ec3e 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 @@ -523,7 +523,9 @@ class StateFragmentPresenter @Inject constructor( private fun setHintOpenedAndUnRevealed(isHintUnrevealed: Boolean) { viewModel.setHintOpenedAndUnRevealedVisibility(isHintUnrevealed) - if (!isHintBulbAnimationEnabled.value) return if (isHintUnrevealed) { + if (!isHintBulbAnimationEnabled.value) return + + if (isHintUnrevealed) { val hintBulbAnimation = AnimationUtils.loadAnimation( context, R.anim.hint_bulb_animation 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 e3d2c4ead14..e4ba610c7f2 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 @@ -23,12 +23,18 @@ interface InteractionAnswerHandler { return null } - /** Return the current answer that is ready for handling. */ + /** + * Returns the current answer entered or selected by the user which is used in pending state to + * show list of previously selected answers. + */ fun getPendingAnswer(): UserAnswer? { return null } - /** Return the last answer entered by user. */ + /** + * Returns a raw representation of the current answer entered by the user which is used to retain + * state on configuration changes. + */ fun getRawUserAnswer(): RawUserAnswer { return RawUserAnswer.getDefaultInstance() } 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 fae7faf8e29..344056fdd19 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 @@ -52,6 +52,7 @@ import dagger.BindsInstance import dagger.Component import dagger.Module import dagger.Provides +import kotlinx.android.synthetic.main.drag_drop_single_item.view.* import kotlinx.coroutines.CoroutineDispatcher import org.hamcrest.BaseMatcher import org.hamcrest.CoreMatchers.allOf @@ -198,8 +199,6 @@ import java.io.IOException import java.util.concurrent.TimeoutException import javax.inject.Inject import javax.inject.Singleton -import junit.framework.Assert.assertTrue -import kotlinx.android.synthetic.main.drag_drop_single_item.view.* /** Tests for [StateFragment]. */ @RunWith(AndroidJUnit4::class) @@ -247,8 +246,8 @@ class StateFragmentTest { @Before fun setUp() { -// TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) -// TestPlatformParameterModule.forceEnableHintBulbAnimation(false) + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) + TestPlatformParameterModule.forceEnableHintBulbAnimation(false) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() @@ -1829,7 +1828,7 @@ class StateFragmentTest { position = 2, targetViewId = R.id.drag_drop_item_recyclerview ) - ).check{ view, _ -> + ).check { view, _ -> val textView = view.findViewById(R.id.drag_drop_content_text_view) assertThat(textView.text.toString(), containsString("a camera at the store")) } From 4be01c4927d7aeaf4423f284f166542324a0bc9b Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 9 Nov 2022 20:29:19 +0530 Subject: [PATCH 47/76] Nit changes and fixed issue with mathInputInteraction --- .../state/ImageRegionSelectionInteractionView.kt | 2 +- .../oppia/android/app/player/state/StateFragment.kt | 4 ++-- .../app/player/state/StateFragmentPresenter.kt | 6 +++--- .../player/state/StatePlayerRecyclerViewAssembler.kt | 10 ++++------ .../state/answerhandling/InteractionAnswerHandler.kt | 5 +---- .../MathExpressionInteractionsViewModel.kt | 4 ++-- .../topic/questionplayer/QuestionPlayerFragment.kt | 6 +++--- .../QuestionPlayerFragmentPresenter.kt | 10 +++++----- .../oppia/android/app/utility/ClickableAreasImage.kt | 12 +++++++----- 9 files changed, 28 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index c1a3f3381df..416f609a46c 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -127,7 +127,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( ) areasImage.addRegionViews() if (::lastSelectedRegion.isInitialized && lastSelectedRegion.hasRegion()) { - areasImage.toggleRegion(lastSelectedRegion) + areasImage.toggleRegion(lastSelectedRegion, null) } performAttachment(areasImage) } 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 ce80bdc3f53..cb8c2704491 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 @@ -89,7 +89,7 @@ class StateFragment : val rawUserAnswer = savedInstanceState?.getProto( STATE_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() ) ?: RawUserAnswer.getDefaultInstance() - val isPreviousResponsesExpanded = + val arePreviousResponsesExpanded = savedInstanceState?.getBoolean(STATE_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY) ?: false return stateFragmentPresenter.handleCreateView( @@ -99,7 +99,7 @@ class StateFragment : topicId, storyId, rawUserAnswer, - isPreviousResponsesExpanded, + arePreviousResponsesExpanded, explorationId ) } 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 dba2dc2ec3e..5bb1e6f288b 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 @@ -211,7 +211,6 @@ class StateFragmentPresenter @Inject constructor( } fun onSubmitButtonClicked() { - recyclerViewAssembler.resetRawUserAnswer() hideKeyboard() handleSubmitAnswer(viewModel.getPendingAnswer(recyclerViewAssembler::getPendingAnswerHandler)) } @@ -278,7 +277,7 @@ class StateFragmentPresenter @Inject constructor( subscribeToHintSolution(explorationProgressController.submitSolutionIsRevealed()) } - /** Returns whether previously submitted wrong answers should be expanded or not. */ + /** Returns whether previously submitted wrong answers are currently expanded. */ fun getArePreviousResponsesExpanded(): Boolean { return recyclerViewAssembler.arePreviousResponsesExpanded } @@ -392,6 +391,7 @@ class StateFragmentPresenter @Inject constructor( if (result.labelledAsCorrectAnswer) { recyclerViewAssembler.showCelebrationOnCorrectAnswer(result.feedback) } else { + recyclerViewAssembler.resetRawUserAnswer() viewModel.setCanSubmitAnswer(canSubmitAnswer = false) } recyclerViewAssembler.readOutAnswerFeedback(result.feedback) @@ -473,7 +473,7 @@ class StateFragmentPresenter @Inject constructor( /** Returns the checkpoint state for the current exploration. */ fun getExplorationCheckpointState() = explorationCheckpointState - /** Returns [RawUserAnswer] from stateViewModel's [getRawUserAnswer]. */ + /** Returns the [RawUserAnswer] representing the user's current pending answer. */ fun getRawUserAnswer(): RawUserAnswer { return if (isConfigChangeStateRetentionEnabled.value) { viewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) 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 984630109aa..dcabe2a5c88 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 @@ -1369,10 +1369,8 @@ class StatePlayerRecyclerViewAssembler private constructor( val playerFeatureSet = featureSets.reduce(PlayerFeatureSet::union) val assembler = StatePlayerRecyclerViewAssembler( accessibilityService, - /* adapter= */ - adapterBuilder.build(), - /* rhsAdapter= */ - adapterBuilder.build(), + /* adapter= */ adapterBuilder.build(), + /* rhsAdapter= */ adapterBuilder.build(), playerFeatureSet, fragment, profileId, @@ -1424,7 +1422,7 @@ class StatePlayerRecyclerViewAssembler private constructor( entityType: String, profileId: ProfileId, rawUserAnswer: RawUserAnswer, - isPreviousResponsesExpanded: Boolean + arePreviousResponsesExpanded: Boolean ): Builder { return Builder( accessibilityService, @@ -1441,7 +1439,7 @@ class StatePlayerRecyclerViewAssembler private constructor( multiAdapterBuilderFactory, singleAdapterFactory, rawUserAnswer, - isPreviousResponsesExpanded + arePreviousResponsesExpanded ) } } 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 e4ba610c7f2..10b040578b8 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 @@ -23,10 +23,7 @@ interface InteractionAnswerHandler { return null } - /** - * Returns the current answer entered or selected by the user which is used in pending state to - * show list of previously selected answers. - */ + /** Returns the pending answer awaiting submission by the user. */ fun getPendingAnswer(): UserAnswer? { return null } 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 e26b2c3d7ff..8eb136b0908 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 @@ -108,7 +108,7 @@ class MathExpressionInteractionsViewModel private constructor( errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) - checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) + checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -181,7 +181,7 @@ class MathExpressionInteractionsViewModel private constructor( if (isAnswerTextAvailable != isAnswerAvailable.get()) { isAnswerAvailable.set(isAnswerTextAvailable) } - checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) + checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME) } override fun afterTextChanged(s: Editable) { 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 037c8f9929d..bef59fd66b2 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 @@ -61,13 +61,13 @@ class QuestionPlayerFragment : val rawUserAnswer = savedInstanceState?.getProto( QUESTION_PLAYER_FRAGMENT_RAW_USER_ANSWER_KEY, RawUserAnswer.getDefaultInstance() ) ?: RawUserAnswer.getDefaultInstance() - val isPreviousResponsesExpanded = + val arePreviousResponsesExpanded = savedInstanceState?.getBoolean( QUESTION_PLAYER_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY ) ?: false val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()) return questionPlayerFragmentPresenter.handleCreateView( - inflater, container, rawUserAnswer, isPreviousResponsesExpanded, profileId + inflater, container, rawUserAnswer, arePreviousResponsesExpanded, profileId ) } @@ -106,7 +106,7 @@ class QuestionPlayerFragment : ) outState.putBoolean( QUESTION_PLAYER_FRAGMENT_ARE_PREVIOUS_RESPONSES_HEADER_EXPANDED_KEY, - questionPlayerFragmentPresenter.getIsPreviousResponsesExpanded() + questionPlayerFragmentPresenter.getArePreviousResponsesExpanded() ) } 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 24e4cab9faa..d495c82d4db 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 @@ -74,7 +74,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( inflater: LayoutInflater, container: ViewGroup?, rawUserAnswer: RawUserAnswer, - isPreviousResponsesExpanded: Boolean, + arePreviousResponsesExpanded: Boolean, profileId: ProfileId ): View? { binding = QuestionPlayerFragmentBinding.inflate( @@ -89,7 +89,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( "skill", profileId, rawUserAnswer, - isPreviousResponsesExpanded + arePreviousResponsesExpanded ), binding.congratulationsTextView, binding.congratulationsTextConfettiView, @@ -124,8 +124,8 @@ class QuestionPlayerFragmentPresenter @Inject constructor( subscribeToHintSolution(questionAssessmentProgressController.submitSolutionIsRevealed()) } - /** Returns whether previously submitted wrong answers should be expanded or not. */ - fun getIsPreviousResponsesExpanded(): Boolean { + /** Returns whether previously submitted wrong answers are currently expanded. */ + fun getArePreviousResponsesExpanded(): Boolean { return recyclerViewAssembler.arePreviousResponsesExpanded } @@ -409,7 +409,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } } - /** Returns [RawUserAnswer] from stateViewModel's [getRawUserAnswer]. */ + /** Returns the [RawUserAnswer] representing the user's current pending answer. */ fun getRawUserAnswer(): RawUserAnswer { return if (isConfigChangeStateRetentionEnabled.value) { questionViewModel.getRawUserAnswer(recyclerViewAssembler::getPendingAnswerHandler) diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index 9aca0d44f27..0adf6f5b2ab 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -90,7 +90,7 @@ class ClickableAreasImage( val newView = createSelectableView(clickableArea) newView.setOnTouchListener { _, event -> if (event.action == MotionEvent.ACTION_DOWN) { - toggleRegion(clickableArea) + toggleRegion(clickableArea, newView) } return@setOnTouchListener true } @@ -98,7 +98,7 @@ class ClickableAreasImage( // Make default region visibility gone when talkback enabled to avoid any accidental touch. defaultRegionView.isVisible = false newView.setOnClickListener { - toggleRegion(clickableArea) + toggleRegion(clickableArea, newView) } } } @@ -122,7 +122,7 @@ class ClickableAreasImage( * Toggles whether the clickable region corresponding to the provided [clickableArea] is visible * and available to be clicked. */ - fun toggleRegion(clickableArea: ImageWithRegions.LabeledRegion) { + fun toggleRegion(clickableArea: ImageWithRegions.LabeledRegion, view: View?) { resetRegionSelectionViews() listener.onClickableAreaTouched( NamedRegionClickedEvent( @@ -130,8 +130,10 @@ class ClickableAreasImage( clickableArea.contentDescription ) ) - val newView = createSelectableView(clickableArea) - newView.setBackgroundResource(R.drawable.selected_region_background) + + view?.setBackgroundResource(R.drawable.selected_region_background) ?: createSelectableView( + clickableArea + ).setBackgroundResource(R.drawable.selected_region_background) } private fun createSelectableView(clickableArea: ImageWithRegions.LabeledRegion): View { From fd0eb62fc5f18354cd9a35fae11fd70f7b07fa22 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 9 Nov 2022 20:35:32 +0530 Subject: [PATCH 48/76] Updated comments --- .../app/player/state/StateFragmentTest.kt | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 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 344056fdd19..adcde40c102 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 @@ -1611,9 +1611,9 @@ class StateFragmentTest { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() clickContinueInteractionButton() - // Entering text in Fraction Input Interaction + // Entering text in Fraction Input Interaction. typeFractionText("34k") - // Rotating device + // Rotating device. rotateToLandscape() it.onActivity { val fractionInputInteraction = @@ -1634,9 +1634,9 @@ class StateFragmentTest { playThroughPrototypeState3() playThroughPrototypeState4() playThroughPrototypeState5() - // Entering text in Numeric Input + // Entering text in Numeric Input. typeNumericInput("121") - // Rotating device + // Rotating device. rotateToLandscape() it.onActivity { val numericInputInteractionView = @@ -1657,9 +1657,9 @@ class StateFragmentTest { playThroughPrototypeState4() playThroughPrototypeState5() playThroughPrototypeState6() - // Entering text in Ratio Input Interaction + // Entering text in Ratio Input Interaction. typeRatioExpression("3a:5") - // Rotating device + // Rotating device. rotateToLandscape() it.onActivity { val ratioInputInteraction = @@ -1682,9 +1682,9 @@ class StateFragmentTest { playThroughPrototypeState5() playThroughPrototypeState6() playThroughPrototypeState7() - // Enter text in Text Input Interaction + // Enter text in Text Input Interaction. typeTextInput("finnish") - // Rotating device + // Rotating device. rotateToLandscape() it.onActivity { val textInputInteraction = @@ -1699,9 +1699,9 @@ class StateFragmentTest { fun testStateFragment_mathInteractions_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { startPlayingExploration() - // Enter text in Math Input Interaction + // Enter text in Math Input Interaction. typeNumericExpression("1+2") - // Rotating device + // Rotating device. rotateToLandscape() it.onActivity { val mathExpressionInteractionView = @@ -1862,9 +1862,9 @@ class StateFragmentTest { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() clickContinueInteractionButton() - // Entering text in Fraction Input Interaction + // Entering text in Fraction Input Interaction. typeFractionText("1/2") - // Rotating device + // Rotating device. rotateToLandscape() it.onActivity { val fractionInputInteraction = @@ -1906,7 +1906,7 @@ class StateFragmentTest { onView(withId(R.id.previous_response_header)).perform(click()) testCoroutineDispatchers.runCurrent() - // rotate screen + // Rotating device. rotateToLandscape() // Both failed answers should be showing. @@ -1932,7 +1932,7 @@ class StateFragmentTest { typeFractionText("1/4") clickSubmitAnswerButton() - // rotate screen (by default responses are collapsed) + // Rotate screen (by default responses are collapsed). rotateToLandscape() // Only the latest failed answer should be showing. From 4c1b677a9d5a0bdc904e0e7788c2b151690641ca Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 9 Nov 2022 20:40:35 +0530 Subject: [PATCH 49/76] removed extra space --- .../state/itemviewmodel/MathExpressionInteractionsViewModel.kt | 1 - 1 file changed, 1 deletion(-) 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 8eb136b0908..18004efb34f 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 @@ -105,7 +105,6 @@ class MathExpressionInteractionsViewModel private constructor( ) } } - errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME) From 2f8f8b268f45c380c4d4208738bba8818612fdf0 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 9 Nov 2022 21:04:25 +0530 Subject: [PATCH 50/76] Updated computer error --- .../MathExpressionInteractionsViewModel.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 18004efb34f..f6fdc02130e 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 @@ -107,7 +107,7 @@ class MathExpressionInteractionsViewModel private constructor( } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) - checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME) + checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -153,7 +153,11 @@ class MathExpressionInteractionsViewModel private constructor( if (answerText.isNotEmpty()) { pendingAnswerError = when (category) { // There's no support for real-time errors. - AnswerErrorCategory.REAL_TIME -> null + AnswerErrorCategory.REAL_TIME -> { + interactionType.computeSubmitTimeError( + answerText.toString(), allowedVariables, resourceHandler + ) + } AnswerErrorCategory.SUBMIT_TIME -> { interactionType.computeSubmitTimeError( answerText.toString(), allowedVariables, resourceHandler @@ -180,7 +184,7 @@ class MathExpressionInteractionsViewModel private constructor( if (isAnswerTextAvailable != isAnswerAvailable.get()) { isAnswerAvailable.set(isAnswerTextAvailable) } - checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME) + checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } override fun afterTextChanged(s: Editable) { From a5a2496ba298f2211a2bf1bab80b89992078a377 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 9 Nov 2022 22:10:37 +0530 Subject: [PATCH 51/76] Removed test to check Real time error is disabled --- .../MathExpressionInteractionsViewTest.kt | 25 ------------------- 1 file changed, 25 deletions(-) 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 a74cf4af05b..3956cf46e30 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 @@ -1600,31 +1600,6 @@ class MathExpressionInteractionsViewTest { } } - @Test - @RunParameterized( - Iteration("numeric_expression_valid", "type=NUMERIC_EXPRESSION", "text=0/1"), - Iteration("numeric_expression_invalid", "type=NUMERIC_EXPRESSION", "text=1/0"), - Iteration("algebraic_expression_valid", "type=ALGEBRAIC_EXPRESSION", "text=x^2"), - Iteration("algebraic_expression_invalid", "type=ALGEBRAIC_EXPRESSION", "text=2^x"), - Iteration("math_equation_valid", "type=MATH_EQUATION", "text=z=x^2"), - Iteration("math_equation_invalid", "type=MATH_EQUATION", "text=z=2^x") - ) - fun testView_allInteractions_validAndInvalidExpressions_doNotProduceRealTimeError() { - val interactionType = MathInteractionType.valueOf(type) - val interaction = createInteraction() - launch(interactionType, interaction).use { scenario -> - testCoroutineDispatchers.runCurrent() - - typeExpressionInput(text) - - // Using not-allowed-listed variables should result in a failure. - scenario.onActivity { activity -> - val answerError = activity.mathExpressionViewModel.checkPendingAnswerError(REAL_TIME) - assertThat(answerError).isNull() - } - } - } - private fun launchForNumericExpressions( interaction: Interaction = Interaction.getDefaultInstance(), translationContext: WrittenTranslationContext = WrittenTranslationContext.getDefaultInstance() From 175d1b21ae4a0f3c3c1593592cbaf26a4e738851 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 9 Nov 2022 22:37:12 +0530 Subject: [PATCH 52/76] Updated tests --- .../MathExpressionInteractionsViewTest.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) 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 3956cf46e30..3989f154c01 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 @@ -1600,6 +1600,31 @@ class MathExpressionInteractionsViewTest { } } + @Test + @RunParameterized( + Iteration("numeric_expression_valid", "type=NUMERIC_EXPRESSION", "text=0/1"), + Iteration("numeric_expression_invalid", "type=NUMERIC_EXPRESSION", "text=1/0"), + Iteration("algebraic_expression_valid", "type=ALGEBRAIC_EXPRESSION", "text=x^2"), + Iteration("algebraic_expression_invalid", "type=ALGEBRAIC_EXPRESSION", "text=2^x"), + Iteration("math_equation_valid", "type=MATH_EQUATION", "text=z=x^2"), + Iteration("math_equation_invalid", "type=MATH_EQUATION", "text=z=2^x") + ) + fun testView_allInteractions_validAndInvalidExpressions_produceRealTimeError() { + val interactionType = MathInteractionType.valueOf(type) + val interaction = createInteraction() + launch(interactionType, interaction).use { scenario -> + testCoroutineDispatchers.runCurrent() + + typeExpressionInput(text) + + // Using not-allowed-listed variables should result in a failure. + scenario.onActivity { activity -> + val answerError = activity.mathExpressionViewModel.checkPendingAnswerError(REAL_TIME) + assertThat(answerError.toString()).isNotNull() + } + } + } + private fun launchForNumericExpressions( interaction: Interaction = Interaction.getDefaultInstance(), translationContext: WrittenTranslationContext = WrittenTranslationContext.getDefaultInstance() From 54d6f4766f3b6f28d159ebb60975ba12625ce14c Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Wed, 9 Nov 2022 22:41:52 +0530 Subject: [PATCH 53/76] Removed comment --- .../state/itemviewmodel/MathExpressionInteractionsViewModel.kt | 1 - 1 file changed, 1 deletion(-) 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 f6fdc02130e..f79b093bfce 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 @@ -152,7 +152,6 @@ class MathExpressionInteractionsViewModel private constructor( override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { if (answerText.isNotEmpty()) { pendingAnswerError = when (category) { - // There's no support for real-time errors. AnswerErrorCategory.REAL_TIME -> { interactionType.computeSubmitTimeError( answerText.toString(), allowedVariables, resourceHandler From 78ea42541d708c6e4b58bcbeff6ab0b85e134299 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 10 Nov 2022 16:56:30 +0530 Subject: [PATCH 54/76] Re-run CI checks From cca3fb251dca0757f36be21ac354337754e6923a Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 10 Nov 2022 17:10:49 +0530 Subject: [PATCH 55/76] Nit changes --- .../app/player/state/ImageRegionSelectionInteractionView.kt | 2 +- .../org/oppia/android/app/utility/ClickableAreasImage.kt | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt index 416f609a46c..c4064673a4b 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/ImageRegionSelectionInteractionView.kt @@ -127,7 +127,7 @@ class ImageRegionSelectionInteractionView @JvmOverloads constructor( ) areasImage.addRegionViews() if (::lastSelectedRegion.isInitialized && lastSelectedRegion.hasRegion()) { - areasImage.toggleRegion(lastSelectedRegion, null) + areasImage.toggleRegion(lastSelectedRegion, view = null) } performAttachment(areasImage) } diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index 0adf6f5b2ab..ad9dabaca99 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -131,9 +131,8 @@ class ClickableAreasImage( ) ) - view?.setBackgroundResource(R.drawable.selected_region_background) ?: createSelectableView( - clickableArea - ).setBackgroundResource(R.drawable.selected_region_background) + val affectedView = view ?: createSelectableView(clickableArea) + affectedView.setBackgroundResource(R.drawable.selected_region_background) } private fun createSelectableView(clickableArea: ImageWithRegions.LabeledRegion): View { From d08a7cc76694b3e33e33e25fd28e27039ef5f99d Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Thu, 10 Nov 2022 23:53:37 +0530 Subject: [PATCH 56/76] Fixed app crash error --- .../android/app/player/state/StateFragmentPresenter.kt | 2 +- .../itemviewmodel/MathExpressionInteractionsViewModel.kt | 7 +------ 2 files changed, 2 insertions(+), 7 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 5bb1e6f288b..c7a4e1b7c89 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 @@ -449,7 +449,7 @@ class StateFragmentPresenter @Inject constructor( val inputManager: InputMethodManager = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager inputManager.hideSoftInputFromWindow( - fragment.view!!.windowToken, + fragment.view?.windowToken, InputMethodManager.SHOW_FORCED ) } 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 f79b093bfce..8df6b88d4ac 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 @@ -107,7 +107,6 @@ class MathExpressionInteractionsViewModel private constructor( } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) - checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -152,11 +151,7 @@ class MathExpressionInteractionsViewModel private constructor( override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { if (answerText.isNotEmpty()) { pendingAnswerError = when (category) { - AnswerErrorCategory.REAL_TIME -> { - interactionType.computeSubmitTimeError( - answerText.toString(), allowedVariables, resourceHandler - ) - } + AnswerErrorCategory.REAL_TIME -> null AnswerErrorCategory.SUBMIT_TIME -> { interactionType.computeSubmitTimeError( answerText.toString(), allowedVariables, resourceHandler From bc0f45d11e23f6b3b6e1e328afa1a177fd47f225 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 11 Nov 2022 00:18:58 +0530 Subject: [PATCH 57/76] reverted back changes in tests --- .../interaction/MathExpressionInteractionsViewTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 3989f154c01..91675e86865 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 @@ -1609,7 +1609,7 @@ class MathExpressionInteractionsViewTest { Iteration("math_equation_valid", "type=MATH_EQUATION", "text=z=x^2"), Iteration("math_equation_invalid", "type=MATH_EQUATION", "text=z=2^x") ) - fun testView_allInteractions_validAndInvalidExpressions_produceRealTimeError() { + fun testView_allInteractions_validAndInvalidExpressions_doNotProduceRealTimeError() { val interactionType = MathInteractionType.valueOf(type) val interaction = createInteraction() launch(interactionType, interaction).use { scenario -> @@ -1620,7 +1620,7 @@ class MathExpressionInteractionsViewTest { // Using not-allowed-listed variables should result in a failure. scenario.onActivity { activity -> val answerError = activity.mathExpressionViewModel.checkPendingAnswerError(REAL_TIME) - assertThat(answerError.toString()).isNotNull() + assertThat(answerError).isNull() } } } @@ -1806,4 +1806,4 @@ class MathExpressionInteractionsViewTest { override fun getApplicationInjector(): ApplicationInjector = component } -} +} \ No newline at end of file From 412c71ca5585b402540044cf52ed9bc6b57749d5 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 11 Nov 2022 02:17:06 +0530 Subject: [PATCH 58/76] Updated resetRawUserAnswer --- .../oppia/android/app/player/state/StateFragmentPresenter.kt | 4 +++- .../topic/questionplayer/QuestionPlayerFragmentPresenter.kt | 4 +++- .../interaction/MathExpressionInteractionsViewTest.kt | 2 +- 3 files changed, 7 insertions(+), 3 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 c7a4e1b7c89..3c534c0e7d4 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 @@ -379,6 +379,9 @@ class StateFragmentPresenter @Inject constructor( private fun subscribeToAnswerOutcome( answerOutcomeResultLiveData: LiveData> ) { + if (viewModel.getCanSubmitAnswer().get() == true) { + recyclerViewAssembler.resetRawUserAnswer() + } val answerOutcomeLiveData = getAnswerOutcome(answerOutcomeResultLiveData) answerOutcomeLiveData.observe( fragment, @@ -391,7 +394,6 @@ class StateFragmentPresenter @Inject constructor( if (result.labelledAsCorrectAnswer) { recyclerViewAssembler.showCelebrationOnCorrectAnswer(result.feedback) } else { - recyclerViewAssembler.resetRawUserAnswer() viewModel.setCanSubmitAnswer(canSubmitAnswer = false) } recyclerViewAssembler.readOutAnswerFeedback(result.feedback) 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 d495c82d4db..f35009c34a7 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 @@ -161,7 +161,6 @@ class QuestionPlayerFragmentPresenter @Inject constructor( } fun onSubmitButtonClicked() { - recyclerViewAssembler.resetRawUserAnswer() hideKeyboard() handleSubmitAnswer( questionViewModel.getPendingAnswer( @@ -278,6 +277,9 @@ class QuestionPlayerFragmentPresenter @Inject constructor( private fun subscribeToAnswerOutcome( answerOutcomeResultLiveData: LiveData> ) { + if (questionViewModel.getCanSubmitAnswer().get() == true) { + recyclerViewAssembler.resetRawUserAnswer() + } val answerOutcomeLiveData = Transformations.map(answerOutcomeResultLiveData, ::processAnsweredQuestionOutcome) answerOutcomeLiveData.observe( 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 91675e86865..a74cf4af05b 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 @@ -1806,4 +1806,4 @@ class MathExpressionInteractionsViewTest { override fun getApplicationInjector(): ApplicationInjector = component } -} \ No newline at end of file +} From ad918699017ab6982d19976ee366692749d48948 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 11 Nov 2022 02:29:36 +0530 Subject: [PATCH 59/76] Fixed nit changes --- .../testing/platformparameter/TestPlatformParameterModule.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt index 1d2db12ee14..0485662af48 100644 --- a/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt +++ b/testing/src/main/java/org/oppia/android/testing/platformparameter/TestPlatformParameterModule.kt @@ -9,7 +9,6 @@ import org.oppia.android.util.platformparameter.CacheLatexRendering import org.oppia.android.util.platformparameter.ENABLE_DOWNLOADS_SUPPORT_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_EDIT_ACCOUNTS_OPTIONS_UI_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_EXTRA_TOPIC_TABS_UI_DEFAULT_VALUE - import org.oppia.android.util.platformparameter.ENABLE_HINT_BULB_ANIMATION import org.oppia.android.util.platformparameter.ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION_DEFAULT_VALUE import org.oppia.android.util.platformparameter.ENABLE_LANGUAGE_SELECTION_UI_DEFAULT_VALUE From f5acc868fbabd8062ae8a06389b62c63c3a39e53 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Fri, 11 Nov 2022 09:12:20 +0530 Subject: [PATCH 60/76] .replace() used --- .../app/player/exploration/ExplorationActivityPresenter.kt | 2 +- .../state/itemviewmodel/MathExpressionInteractionsViewModel.kt | 1 + .../util/platformparameter/PlatformParameterConstants.kt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt index 15620778703..b5189a708c1 100644 --- a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt @@ -128,7 +128,7 @@ class ExplorationActivityPresenter @Inject constructor( fun loadExplorationFragment(readingTextSize: ReadingTextSize) { if (getExplorationFragment() == null) { - activity.supportFragmentManager.beginTransaction().add( + activity.supportFragmentManager.beginTransaction().replace( R.id.exploration_fragment_placeholder, ExplorationFragment.newInstance( profileId, topicId, storyId, explorationId, readingTextSize 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 8df6b88d4ac..14831c345ef 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 @@ -151,6 +151,7 @@ class MathExpressionInteractionsViewModel private constructor( override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { if (answerText.isNotEmpty()) { pendingAnswerError = when (category) { + // There's no support for real-time errors. AnswerErrorCategory.REAL_TIME -> null AnswerErrorCategory.SUBMIT_TIME -> { interactionType.computeSubmitTimeError( diff --git a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt index 2c849809ff7..5544fb9f9e3 100644 --- a/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt +++ b/utility/src/main/java/org/oppia/android/util/platformparameter/PlatformParameterConstants.kt @@ -219,7 +219,7 @@ annotation class EnableInteractionConfigChangeStateRetention /** * Default value for feature flag corresponding to [EnableInteractionConfigChangeStateRetention]. */ -const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION_DEFAULT_VALUE = false +const val ENABLE_INTERACTION_CONFIG_CHANGE_STATE_RETENTION_DEFAULT_VALUE = true /** * Qualifier for the platform parameter that controls the animation for hint bulb animation From 99e087e3df7451d2468eecd8f1f200d9d4a07d75 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 12 Nov 2022 05:40:12 +0530 Subject: [PATCH 61/76] ToDo added --- .../state/itemviewmodel/MathExpressionInteractionsViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 14831c345ef..b9ef623f952 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 = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = "" //TODO(#4708): Need to retain state for submit time error. /** * Defines whether an answer is currently available to parse. This is expected to be directly From 12495d5d8a8f44e755906e5db430c2dc9abad17b Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 12 Nov 2022 05:40:59 +0530 Subject: [PATCH 62/76] added space --- .../state/itemviewmodel/MathExpressionInteractionsViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b9ef623f952..82aac6eb2e9 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 = "" //TODO(#4708): Need to retain state for submit time error. + var answerText: CharSequence = "" // TODO(#4708): Need to retain state for submit time error. /** * Defines whether an answer is currently available to parse. This is expected to be directly From d23e82d0812c43719c55d986339eff773e424fa4 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 12 Nov 2022 05:49:11 +0530 Subject: [PATCH 63/76] removed math interaction tests --- .../app/player/state/StateFragmentTest.kt | 42 +------------------ 1 file changed, 1 insertion(+), 41 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 adcde40c102..b224023bb2b 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 @@ -1694,25 +1694,6 @@ class StateFragmentTest { } } - @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions - @RunOn(TestPlatform.ESPRESSO) - fun testStateFragment_mathInteractions_retainStateOnConfigurationChange() { - launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { - startPlayingExploration() - // Enter text in Math Input Interaction. - typeNumericExpression("1+2") - // Rotating device. - rotateToLandscape() - it.onActivity { - val mathExpressionInteractionView = - it.findViewById( - R.id.math_expression_input_interaction_view - ) - assertThat(mathExpressionInteractionView.text.toString()).isEqualTo("1+2") - } - } - } - @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_selectionInteraction_ratioButton_retainStateOnConfigurationChange() { @@ -1837,28 +1818,7 @@ class StateFragmentTest { @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) - fun testStateFragment_sameTextBasedInteractions_doesNotShareInitialState() { - launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { - startPlayingExploration() - - typeNumericExpression("1+2") - rotateToLandscape() - clickSubmitAnswerButton() - clickContinueNavigationButton() - - it.onActivity { - val mathExpressionInteractionView = - it.findViewById( - R.id.math_expression_input_interaction_view - ) - assertThat(mathExpressionInteractionView.text.toString().isEmpty()).isTrue() - } - } - } - - @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions - @RunOn(TestPlatform.ESPRESSO) - fun testStateFragment_differentTextBaseInteractions_doesNotShareInitialState() { + fun testStateFragment_differentTextBasedInteractions_doesNotShareInitialState() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() clickContinueInteractionButton() From 23e99e5060228f015c5089dc5d657b185b78bacc Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 12 Nov 2022 05:50:12 +0530 Subject: [PATCH 64/76] Imports optimized --- .../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 b224023bb2b..3b09f59c45d 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 @@ -79,7 +79,6 @@ import org.oppia.android.app.application.ApplicationModule import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule 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 From 802e0e12336c3ae59cb3464b86a9ebc58c5a6755 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sat, 12 Nov 2022 07:15:06 +0530 Subject: [PATCH 65/76] Removed unnecessary import --- .../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 9291ea89eff..5feb1ca5a37 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 @@ -52,7 +52,6 @@ import dagger.BindsInstance import dagger.Component import dagger.Module import dagger.Provides -import kotlinx.android.synthetic.main.drag_drop_single_item.view.* import kotlinx.coroutines.CoroutineDispatcher import org.hamcrest.BaseMatcher import org.hamcrest.CoreMatchers.allOf From d79221e498169dd9414acc206fe8bfea4103e199 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sun, 13 Nov 2022 02:08:17 +0530 Subject: [PATCH 66/76] Added new test fixed submit time error issue --- .../ExplorationActivityPresenter.kt | 2 +- .../player/state/StateFragmentPresenter.kt | 17 ++-- .../state/StatePlayerRecyclerViewAssembler.kt | 5 +- .../app/player/state/StateViewModel.kt | 2 +- .../InteractionAnswerHandler.kt | 9 +-- .../DragAndDropSortInteractionViewModel.kt | 2 + .../FractionInteractionViewModel.kt | 28 ++++--- ...mageRegionSelectionInteractionViewModel.kt | 2 + .../MathExpressionInteractionsViewModel.kt | 12 ++- .../itemviewmodel/NumericInputViewModel.kt | 12 ++- ...atioExpressionInputInteractionViewModel.kt | 33 ++++---- .../SelectionInteractionViewModel.kt | 2 + .../state/itemviewmodel/TextInputViewModel.kt | 4 +- .../InputInteractionViewTestActivity.kt | 2 +- .../QuestionPlayerFragmentPresenter.kt | 15 +++- .../questionplayer/QuestionPlayerViewModel.kt | 2 +- .../MathExpressionInteractionsViewTest.kt | 4 +- .../app/player/state/StateFragmentTest.kt | 78 ++++++++++++++++++- model/src/main/proto/exploration.proto | 29 +++++-- 19 files changed, 198 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt index a4a27c7d0e4..59144aad858 100644 --- a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt +++ b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivityPresenter.kt @@ -128,7 +128,7 @@ class ExplorationActivityPresenter @Inject constructor( fun loadExplorationFragment(readingTextSize: ReadingTextSize) { if (getExplorationFragment() == null) { - activity.supportFragmentManager.beginTransaction().replace( + activity.supportFragmentManager.beginTransaction().add( R.id.exploration_fragment_placeholder, ExplorationFragment.newInstance( profileId, topicId, storyId, explorationId, readingTextSize 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 3c534c0e7d4..8f43a5e8bfc 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 @@ -180,7 +180,7 @@ class StateFragmentPresenter @Inject constructor( fun handleAnswerReadyForSubmission(answer: UserAnswer) { // An interaction has indicated that an answer is ready for submission. - handleSubmitAnswer(answer) + handleSubmitAnswer(answer, canSubmitAnswer = true) } fun onContinueButtonClicked() { @@ -224,9 +224,7 @@ class StateFragmentPresenter @Inject constructor( fun handleKeyboardAction() { hideKeyboard() - if (viewModel.getCanSubmitAnswer().get() == true) { - handleSubmitAnswer(viewModel.getPendingAnswer(recyclerViewAssembler::getPendingAnswerHandler)) - } + handleSubmitAnswer(viewModel.getPendingAnswer(recyclerViewAssembler::getPendingAnswerHandler)) } fun onHintAvailable(helpIndex: HelpIndex, isCurrentStatePendingState: Boolean) { @@ -425,8 +423,15 @@ class StateFragmentPresenter @Inject constructor( } } - private fun handleSubmitAnswer(answer: UserAnswer) { - subscribeToAnswerOutcome(explorationProgressController.submitAnswer(answer).toLiveData()) + private fun handleSubmitAnswer( + answer: UserAnswer, + canSubmitAnswer: Boolean = viewModel.getCanSubmitAnswer().get() ?: false + ) { + // This check seems to avoid a crash on configuration change when attempting to resubmit answers + // after encountering a submit-time error, but it's also more correct to keep it. + if (canSubmitAnswer) { + subscribeToAnswerOutcome(explorationProgressController.submitAnswer(answer).toLiveData()) + } } fun dismissConceptCard() { 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 dcabe2a5c88..f4a4f2e684d 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 @@ -329,7 +329,10 @@ class StatePlayerRecyclerViewAssembler private constructor( hasPreviousButton, isSplitView.get()!!, writtenTranslationContext - ) + ).also { + // Ensure that potential errors are re-detected in cases of configuration changes. + (it as? InteractionAnswerHandler)?.checkPendingAnswerError(rawUserAnswer.lastErrorCategory) + } } private fun addContentItem( 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 b2e0e2b9979..8bbdd20e475 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 @@ -4,9 +4,9 @@ import androidx.databinding.ObservableField import androidx.databinding.ObservableList import androidx.lifecycle.ViewModel import org.oppia.android.app.fragment.FragmentScope +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer -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 10b040578b8..0eecb3d7516 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer @@ -44,11 +45,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/DragAndDropSortInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt index f19eeff4799..70de7b8d7df 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt @@ -3,6 +3,7 @@ package org.oppia.android.app.player.state.itemviewmodel import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.recyclerview.widget.RecyclerView +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 @@ -143,6 +144,7 @@ class DragAndDropSortInteractionViewModel private constructor( ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply { addAllContentIdLists(htmlContentIds) }.build() + lastErrorCategory = AnswerErrorCategory.NO_ERROR }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ 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 1c698e772d3..2eb86fe9fd7 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer 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 @@ -32,11 +32,12 @@ class FractionInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.FRACTION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") val hintText: CharSequence = deriveHintText(interaction) private val fractionParser = FractionParser() + private var currentErrorCategory = AnswerErrorCategory.NO_ERROR init { val callback: Observable.OnPropertyChangedCallback = @@ -50,7 +51,6 @@ class FractionInteractionViewModel private constructor( } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) - checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -67,20 +67,23 @@ 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? { if (answerText.isNotEmpty()) { - when (category) { + pendingAnswerError = when (category) { AnswerErrorCategory.REAL_TIME -> { - pendingAnswerError = - FractionParsingUiError.createFromParsingError( - fractionParser.getRealTimeAnswerError(answerText.toString()) - ).getErrorMessageFromStringRes(resourceHandler) + FractionParsingUiError.createFromParsingError( + fractionParser.getRealTimeAnswerError(answerText.toString()) + ).getErrorMessageFromStringRes(resourceHandler) } AnswerErrorCategory.SUBMIT_TIME -> { - pendingAnswerError = - FractionParsingUiError.createFromParsingError( - fractionParser.getSubmitTimeError(answerText.toString()) - ).getErrorMessageFromStringRes(resourceHandler) + FractionParsingUiError.createFromParsingError( + fractionParser.getSubmitTimeError(answerText.toString()) + ).getErrorMessageFromStringRes(resourceHandler) } + AnswerErrorCategory.ANSWER_ERROR_CATEGORY_UNSPECIFIED, AnswerErrorCategory.UNRECOGNIZED, + AnswerErrorCategory.NO_ERROR -> null } + currentErrorCategory = if (pendingAnswerError == null) { + AnswerErrorCategory.NO_ERROR + } else category errorMessage.set(pendingAnswerError) } return pendingAnswerError @@ -89,6 +92,7 @@ class FractionInteractionViewModel private constructor( override fun getRawUserAnswer(): RawUserAnswer = RawUserAnswer.newBuilder().apply { if (answerText.isNotEmpty()) { textualAnswer = answerText.toString() + lastErrorCategory = currentErrorCategory } }.build() 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 9f45ba24820..b084f76a36d 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 @@ -3,6 +3,7 @@ package org.oppia.android.app.player.state.itemviewmodel 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.LabeledRegion import org.oppia.android.app.model.Interaction @@ -89,6 +90,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( if (answerText.isNotEmpty()) { imageRegionSelection = selectableRegions.find { it.label == answerText.toString() } } + lastErrorCategory = AnswerErrorCategory.NO_ERROR }.build() private fun parseClickOnImage(answerTextString: String): ClickOnImage { 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 82aac6eb2e9..b505e432fff 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.RawUserAnswer 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.InteractionAnswerHandler import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver @@ -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 = "" // TODO(#4708): Need to retain state for submit time error. + var answerText: CharSequence = rawUserAnswer.textualAnswer /** * Defines whether an answer is currently available to parse. This is expected to be directly @@ -91,6 +91,8 @@ class MathExpressionInteractionsViewModel private constructor( /** Specifies the text to show in the answer box when no text is entered. */ val hintText: CharSequence = deriveHintText(interaction) + private var currentErrorCategory = AnswerErrorCategory.NO_ERROR + private val allowedVariables = retrieveAllowedVariables(interaction) private val useFractionsForDivision = interaction.customizationArgsMap["useFractionForDivision"]?.boolValue ?: false @@ -146,6 +148,7 @@ class MathExpressionInteractionsViewModel private constructor( if (answerText.isNotEmpty()) { textualAnswer = answerText.toString() } + lastErrorCategory = currentErrorCategory }.build() override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { @@ -158,7 +161,12 @@ class MathExpressionInteractionsViewModel private constructor( answerText.toString(), allowedVariables, resourceHandler ) } + AnswerErrorCategory.ANSWER_ERROR_CATEGORY_UNSPECIFIED, AnswerErrorCategory.UNRECOGNIZED, + AnswerErrorCategory.NO_ERROR -> null } + currentErrorCategory = if (pendingAnswerError == null) { + AnswerErrorCategory.NO_ERROR + } else category 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 112928ea665..8a4b77f6e59 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer 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 @@ -26,11 +26,12 @@ class NumericInputViewModel private constructor( private val writtenTranslationContext: WrittenTranslationContext, private val resourceHandler: AppLanguageResourceHandler ) : StateItemViewModel(ViewType.NUMERIC_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer private var pendingAnswerError: String? = null val errorMessage = ObservableField("") var isAnswerAvailable = ObservableField(false) private val stringToNumberParser: StringToNumberParser = StringToNumberParser() + private var currentErrorCategory = AnswerErrorCategory.NO_ERROR init { val callback: Observable.OnPropertyChangedCallback = @@ -45,7 +46,6 @@ class NumericInputViewModel private constructor( errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) - checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) } /** It checks the pending error for the current numeric input, and correspondingly updates the error string based on the specified error category. */ @@ -58,8 +58,13 @@ class NumericInputViewModel private constructor( AnswerErrorCategory.SUBMIT_TIME -> stringToNumberParser.getSubmitTimeError(answerText.toString()) .getErrorMessageFromStringRes(resourceHandler) + AnswerErrorCategory.ANSWER_ERROR_CATEGORY_UNSPECIFIED, AnswerErrorCategory.UNRECOGNIZED, + AnswerErrorCategory.NO_ERROR -> null } } + currentErrorCategory = if (pendingAnswerError == null) { + AnswerErrorCategory.NO_ERROR + } else category errorMessage.set(pendingAnswerError) return pendingAnswerError } @@ -98,6 +103,7 @@ class NumericInputViewModel private constructor( if (answerText.isNotEmpty()) { textualAnswer = answerText.toString() } + lastErrorCategory = currentErrorCategory }.build() /** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */ 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 62f12344e3a..e50bfbd7a90 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer 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 @@ -33,9 +33,10 @@ class RatioExpressionInputInteractionViewModel private constructor( private val translationController: TranslationController ) : StateItemViewModel(ViewType.RATIO_EXPRESSION_INPUT_INTERACTION), InteractionAnswerHandler { private var pendingAnswerError: String? = null - var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer var isAnswerAvailable = ObservableField(false) var errorMessage = ObservableField("") + private var currentErrorCategory = AnswerErrorCategory.NO_ERROR val hintText: CharSequence = deriveHintText(interaction) private val stringToRatioParser: StringToRatioParser = StringToRatioParser() @@ -75,23 +76,29 @@ class RatioExpressionInputInteractionViewModel private constructor( if (answerText.isNotEmpty()) { textualAnswer = answerText.toString() } + lastErrorCategory = currentErrorCategory }.build() /** It checks the pending error for the current ratio input, and correspondingly updates the error string based on the specified error category. */ override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { if (answerText.isNotEmpty()) { - when (category) { - AnswerErrorCategory.REAL_TIME -> - pendingAnswerError = - stringToRatioParser.getRealTimeAnswerError(answerText.toString()) - .getErrorMessageFromStringRes(resourceHandler) - AnswerErrorCategory.SUBMIT_TIME -> - pendingAnswerError = - stringToRatioParser.getSubmitTimeError( - answerText.toString(), - numberOfTerms = numberOfTerms - ).getErrorMessageFromStringRes(resourceHandler) + pendingAnswerError = when (category) { + AnswerErrorCategory.REAL_TIME -> { + stringToRatioParser.getRealTimeAnswerError(answerText.toString()) + .getErrorMessageFromStringRes(resourceHandler) + } + AnswerErrorCategory.SUBMIT_TIME -> { + stringToRatioParser.getSubmitTimeError( + answerText.toString(), + numberOfTerms = numberOfTerms + ).getErrorMessageFromStringRes(resourceHandler) + } + AnswerErrorCategory.ANSWER_ERROR_CATEGORY_UNSPECIFIED, AnswerErrorCategory.UNRECOGNIZED, + AnswerErrorCategory.NO_ERROR -> null } + currentErrorCategory = if (pendingAnswerError == null) { + AnswerErrorCategory.NO_ERROR + } else category 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 ed026a3fd74..4474bc57cca 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 @@ -3,6 +3,7 @@ package org.oppia.android.app.player.state.itemviewmodel import androidx.databinding.Observable import androidx.databinding.ObservableField import androidx.databinding.ObservableList +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.ItemSelectionRawAnswer @@ -114,6 +115,7 @@ class SelectionInteractionViewModel private constructor( itemSelection = ItemSelectionRawAnswer.newBuilder().apply { addAllSelectedIndexes(selectedItems) }.build() + lastErrorCategory = AnswerErrorCategory.NO_ERROR }.build() /** Returns an HTML list containing all of the HTML string elements as items in the list. */ 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 fb8e3da8bbd..5a71f22ee1f 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 @@ -5,6 +5,7 @@ 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.RawUserAnswer @@ -28,7 +29,7 @@ class TextInputViewModel private constructor( private val resourceHandler: AppLanguageResourceHandler, private val translationController: TranslationController ) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler { - var answerText: CharSequence = rawUserAnswer.textualAnswer ?: "" + var answerText: CharSequence = rawUserAnswer.textualAnswer val hintText: CharSequence = deriveHintText(interaction) var isAnswerAvailable = ObservableField(false) @@ -79,6 +80,7 @@ class TextInputViewModel private constructor( if (answerText.isNotEmpty()) { textualAnswer = answerText.toString() } + lastErrorCategory = AnswerErrorCategory.NO_ERROR }.build() private fun deriveHintText(interaction: Interaction): CharSequence { 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 9bd66235064..7e4cae9c37a 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 @@ -11,6 +11,7 @@ import org.oppia.android.app.activity.InjectableAppCompatActivity import org.oppia.android.app.customview.interaction.FractionInputInteractionView import org.oppia.android.app.customview.interaction.NumericInputInteractionView import org.oppia.android.app.customview.interaction.TextInputInteractionView +import org.oppia.android.app.model.AnswerErrorCategory import org.oppia.android.app.model.InputInteractionViewTestActivityParams import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathInteractionType.ALGEBRAIC_EXPRESSION import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathInteractionType.MATH_EQUATION @@ -22,7 +23,6 @@ import org.oppia.android.app.model.RawUserAnswer 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.FractionInteractionViewModel 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 f35009c34a7..9a43a1ca276 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 @@ -139,7 +139,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor( fun handleAnswerReadyForSubmission(answer: UserAnswer) { // An interaction has indicated that an answer is ready for submission. - handleSubmitAnswer(answer) + handleSubmitAnswer(answer, canSubmitAnswer = true) } fun onContinueButtonClicked() { @@ -269,8 +269,17 @@ class QuestionPlayerFragmentPresenter @Inject constructor( binding.endSessionBodyTextView.visibility = endSessionViewsVisibility } - private fun handleSubmitAnswer(answer: UserAnswer) { - subscribeToAnswerOutcome(questionAssessmentProgressController.submitAnswer(answer).toLiveData()) + private fun handleSubmitAnswer( + answer: UserAnswer, + canSubmitAnswer: Boolean = questionViewModel.getCanSubmitAnswer().get() ?: false + ) { + // This check seems to avoid a crash on configuration change when attempting to resubmit answers + // after encountering a submit-time error, but it's also more correct to keep it. + if (canSubmitAnswer) { + subscribeToAnswerOutcome( + questionAssessmentProgressController.submitAnswer(answer).toLiveData() + ) + } } /** This function listens to and processes the result of submitAnswer from QuestionAssessmentProgressController. */ 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 150a7c32659..aa3882ea748 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.RawUserAnswer import org.oppia.android.app.model.UserAnswer -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 304b2f7310c..73ac1981acd 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 @@ -30,6 +30,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.InputInteractionViewTestActivityParams import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathInteractionType @@ -40,8 +42,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.InputInteractionViewTestActivity 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 5feb1ca5a37..65dda5ca162 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 @@ -25,6 +25,7 @@ import androidx.test.espresso.action.GeneralLocation import androidx.test.espresso.action.Press import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.replaceText import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions.scrollToHolder @@ -78,6 +79,7 @@ import org.oppia.android.app.application.ApplicationModule import org.oppia.android.app.application.ApplicationStartupListenerModule import org.oppia.android.app.application.testing.TestingBuildFlavorModule 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 @@ -244,8 +246,8 @@ class StateFragmentTest { @Before fun setUp() { - TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) - TestPlatformParameterModule.forceEnableHintBulbAnimation(false) +// TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) +// TestPlatformParameterModule.forceEnableHintBulbAnimation(false) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() @@ -1843,6 +1845,27 @@ class StateFragmentTest { } } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_sameTextBasedInteractions_doesNotShareInitialState() { + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + startPlayingExploration() + + typeNumericExpression("1+2") + rotateToLandscape() + clickSubmitAnswerButton() + clickContinueNavigationButton() + + it.onActivity { + val mathExpressionInteractionView = + it.findViewById( + R.id.math_expression_input_interaction_view + ) + assertThat(mathExpressionInteractionView.text.toString().isEmpty()).isTrue() + } + } + } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_checkPreviousHeaderExpanded_retainStateOnConfigChange() { @@ -1875,6 +1898,57 @@ class StateFragmentTest { } } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_onSubmitTimeError_retainStateOnConfigChange() { + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + startPlayingExploration() + + typeNumericExpression("a") + onView(withId(R.id.submit_answer_button)).perform(click()) + + // Rotating device. + rotateToLandscape() + + it.onActivity { + val mathExpressionInteractionView = + it.findViewById( + R.id.math_expression_input_interaction_view + ) + assertThat(mathExpressionInteractionView.text.toString()).isEqualTo("a") + } + onView(withId(R.id.math_expression_input_error)).check(matches(isDisplayed())) + } + } + + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_onSubmitTimeError_submitIncorrectAnswer_doesNotShareInitialState() { + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + startPlayingExploration() + typeNumericExpression("12a") + onView(withId(R.id.submit_answer_button)).perform(click()) + + // Rotating device. + rotateToLandscape() + + scrollToViewType(NUMERIC_EXPRESSION_INPUT_INTERACTION) + onView(withId(R.id.math_expression_input_interaction_view)).perform(replaceText("12")) + testCoroutineDispatchers.runCurrent() + scrollToViewType(SUBMIT_ANSWER_BUTTON) + onView(withId(R.id.submit_answer_button)).perform(click()) + testCoroutineDispatchers.runCurrent() + + it.onActivity { + val mathExpressionInteractionView = + it.findViewById( + R.id.math_expression_input_interaction_view + ) + assertThat(mathExpressionInteractionView.text.toString()).isEmpty() + } + } + } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_checkPreviousHeaderCollapsed_retainStateOnConfigChange() { diff --git a/model/src/main/proto/exploration.proto b/model/src/main/proto/exploration.proto index ab0e42cd882..af183497b15 100644 --- a/model/src/main/proto/exploration.proto +++ b/model/src/main/proto/exploration.proto @@ -328,20 +328,39 @@ message ItemSelectionRawAnswer { } // Corresponds to a raw representation of the current answer entered by the user which is used to -// retain state on configuration changes. +// retain state on configuration changes. Note that this structure is semi-ephemeral in that it will +// only ever be stored on-disk by Android and never by the app. message RawUserAnswer { + // Represents the last error state the user's pending answer was in (to help with reconstituting + // the answer's state). + AnswerErrorCategory last_error_category = 1; oneof answer_input_type { // A raw answer entered by user in a text-based interactions. - string textual_answer = 1; + string textual_answer = 2; // A user's selection for item selection and multiple choice interactions. - ItemSelectionRawAnswer item_selection = 2; + ItemSelectionRawAnswer item_selection = 3; // A user's selection for image region selection interaction. - ImageWithRegions.LabeledRegion image_region_selection = 3; + ImageWithRegions.LabeledRegion image_region_selection = 4; // A user's selected list of set of html content ids in drag and drop interaction. - ListOfSetsOfTranslatableHtmlContentIds list_of_sets_of_translatable_html_content_ids = 4; + ListOfSetsOfTranslatableHtmlContentIds list_of_sets_of_translatable_html_content_ids = 5; } } +// 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; +} + message AnswerAndResponse { // A previous answer the learner submitted. UserAnswer user_answer = 1; From 4725a77d0298ac16d0e2718415dae6d092d8a745 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sun, 13 Nov 2022 08:06:28 +0530 Subject: [PATCH 67/76] Added new tests --- .../player/state/StateFragmentPresenter.kt | 2 +- .../app/player/state/StateFragmentTest.kt | 142 +++++++++++++++--- 2 files changed, 125 insertions(+), 19 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 8f43a5e8bfc..a95037267e0 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 @@ -456,7 +456,7 @@ class StateFragmentPresenter @Inject constructor( val inputManager: InputMethodManager = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager inputManager.hideSoftInputFromWindow( - fragment.view?.windowToken, + fragment.view!!.windowToken, InputMethodManager.SHOW_FORCED ) } 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 65dda5ca162..4e4a5f5a4fb 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 @@ -246,8 +246,8 @@ class StateFragmentTest { @Before fun setUp() { -// TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) -// TestPlatformParameterModule.forceEnableHintBulbAnimation(false) + TestPlatformParameterModule.forceEnableInteractionConfigChangeStateRetention(true) + TestPlatformParameterModule.forceEnableHintBulbAnimation(false) Intents.init() setUpTestApplicationComponent() testCoroutineDispatchers.registerIdlingResource() @@ -1612,15 +1612,14 @@ class StateFragmentTest { startPlayingExploration() clickContinueInteractionButton() // Entering text in Fraction Input Interaction. - typeFractionText("34k") + typeFractionText("34") // Rotating device. rotateToLandscape() it.onActivity { val fractionInputInteraction = it.findViewById(R.id.fraction_input_interaction_view) - assertThat(fractionInputInteraction.text.toString()).isEqualTo("34k") + assertThat(fractionInputInteraction.text.toString()).isEqualTo("34") } - onView(withId(R.id.fraction_input_error)).check(matches(isDisplayed())) } } @@ -1658,15 +1657,14 @@ class StateFragmentTest { playThroughPrototypeState5() playThroughPrototypeState6() // Entering text in Ratio Input Interaction. - typeRatioExpression("3a:5") + typeRatioExpression("3:5") // Rotating device. rotateToLandscape() it.onActivity { val ratioInputInteraction = it.findViewById(R.id.ratio_input_interaction_view) - assertThat(ratioInputInteraction.text.toString()).isEqualTo("3a:5") + assertThat(ratioInputInteraction.text.toString()).isEqualTo("3:5") } - onView(withId(R.id.ratio_input_error)).check(matches(isDisplayed())) } } @@ -1696,7 +1694,7 @@ class StateFragmentTest { @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) - fun testStateFragment_selectionInteraction_ratioButton_retainStateOnConfigurationChange() { + fun testStateFragment_selectMultipleChoiceOption_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() playThroughPrototypeState1() @@ -1718,7 +1716,7 @@ class StateFragmentTest { @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) - fun testStateFragment_selectionInteraction_multipleSelection_retainStateOnConfigurationChange() { + fun testStateFragment_selectItemSelectionCheckbox_retainStateOnConfigurationChange() { launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { startPlayingExploration() playThroughPrototypeState1() @@ -1816,6 +1814,65 @@ class StateFragmentTest { } } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_mathInteractions_numericExp_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + startPlayingExploration() + // Enter text in numeric math input interaction. + typeNumericExpression("1+2") + // Rotating device. + rotateToLandscape() + it.onActivity { + val mathExpressionInteractionView = + it.findViewById( + R.id.math_expression_input_interaction_view + ) + assertThat(mathExpressionInteractionView.text.toString()).isEqualTo("1+2") + } + } + } + + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_mathInteractions_algExp_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + startPlayingExploration() + playUpThroughMathInteractionExplorationState3() + // Enter text in numeric math input interaction. + typeAlgebraicExpression("x^2-x-2") + // Rotating device. + rotateToLandscape() + it.onActivity { + val mathExpressionInteractionView = + it.findViewById( + R.id.math_expression_input_interaction_view + ) + assertThat(mathExpressionInteractionView.text.toString()).isEqualTo("x^2-x-2") + } + } + } + + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_mathInteractions_mathEq_retainStateOnConfigurationChange() { + launchForExploration(TEST_EXPLORATION_ID_5, shouldSavePartialProgress = false).use { + startPlayingExploration() + playUpThroughMathInteractionExplorationState6() + + typeMathEquation("x^2-x-2=2y") + // Rotating device. + rotateToLandscape() + it.onActivity { + val mathExpressionInteractionView = + it.findViewById( + R.id.math_expression_input_interaction_view + ) + assertThat(mathExpressionInteractionView.text.toString()).isEqualTo("x^2-x-2=2y") + } + } + } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_differentTextBasedInteractions_doesNotShareInitialState() { @@ -1840,7 +1897,7 @@ class StateFragmentTest { it.onActivity { val numericInputInteractionView = it.findViewById(R.id.numeric_input_interaction_view) - assertThat(numericInputInteractionView.text.toString().isEmpty()).isTrue() + assertThat(numericInputInteractionView.text.toString()).isEmpty() } } } @@ -1861,7 +1918,7 @@ class StateFragmentTest { it.findViewById( R.id.math_expression_input_interaction_view ) - assertThat(mathExpressionInteractionView.text.toString().isEmpty()).isTrue() + assertThat(mathExpressionInteractionView.text.toString()).isEmpty() } } } @@ -1898,6 +1955,51 @@ class StateFragmentTest { } } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_onRealTimeError_retainStateOnConfigChange() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + clickContinueInteractionButton() + // Entering text in Fraction Input Interaction. + typeFractionText("k") + // Rotating device. + rotateToLandscape() + it.onActivity { + val fractionInputInteraction = + it.findViewById(R.id.fraction_input_interaction_view) + assertThat(fractionInputInteraction.text.toString()).isEqualTo("k") + } + onView(withId(R.id.fraction_input_error)).check(matches(isDisplayed())) + } + } + + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions + @RunOn(TestPlatform.ESPRESSO) + fun testStateFragment_onRealTimeError_submitIncorrectAnswer_doesNotShareInitialState() { + launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use { + startPlayingExploration() + clickContinueInteractionButton() + // Entering text in Fraction Input Interaction. + typeFractionText("k") + // Rotating device. + rotateToLandscape() + + scrollToViewType(FRACTION_INPUT_INTERACTION) + onView(withId(R.id.fraction_input_interaction_view)).perform(replaceText("12")) + testCoroutineDispatchers.runCurrent() + scrollToViewType(SUBMIT_ANSWER_BUTTON) + onView(withId(R.id.submit_answer_button)).perform(click()) + testCoroutineDispatchers.runCurrent() + + it.onActivity { + val fractionInputInteraction = + it.findViewById(R.id.fraction_input_interaction_view) + assertThat(fractionInputInteraction.text.toString()).isEmpty() + } + } + } + @Test // TODO(#4692): Robolectric tests not working on screen rotation for input interactions @RunOn(TestPlatform.ESPRESSO) fun testStateFragment_onSubmitTimeError_retainStateOnConfigChange() { @@ -1910,14 +2012,18 @@ class StateFragmentTest { // Rotating device. rotateToLandscape() + scrollToViewType(FRACTION_INPUT_INTERACTION) + onView(withId(R.id.fraction_input_interaction_view)).perform(replaceText("12")) + testCoroutineDispatchers.runCurrent() + scrollToViewType(SUBMIT_ANSWER_BUTTON) + onView(withId(R.id.submit_answer_button)).perform(click()) + testCoroutineDispatchers.runCurrent() + it.onActivity { - val mathExpressionInteractionView = - it.findViewById( - R.id.math_expression_input_interaction_view - ) - assertThat(mathExpressionInteractionView.text.toString()).isEqualTo("a") + val fractionInputInteraction = + it.findViewById(R.id.fraction_input_interaction_view) + assertThat(fractionInputInteraction.text.toString()).isEmpty() } - onView(withId(R.id.math_expression_input_error)).check(matches(isDisplayed())) } } From 6b59e1a5ef7f429b5d8400df58c7cf935560ddb3 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sun, 13 Nov 2022 08:11:46 +0530 Subject: [PATCH 68/76] Removed unncessary changes --- .../android/app/player/state/StateFragmentTest.kt | 13 +++---------- 1 file changed, 3 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 4e4a5f5a4fb..10d66890961 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 @@ -2012,17 +2012,10 @@ class StateFragmentTest { // Rotating device. rotateToLandscape() - scrollToViewType(FRACTION_INPUT_INTERACTION) - onView(withId(R.id.fraction_input_interaction_view)).perform(replaceText("12")) - testCoroutineDispatchers.runCurrent() - scrollToViewType(SUBMIT_ANSWER_BUTTON) - onView(withId(R.id.submit_answer_button)).perform(click()) - testCoroutineDispatchers.runCurrent() - it.onActivity { - val fractionInputInteraction = - it.findViewById(R.id.fraction_input_interaction_view) - assertThat(fractionInputInteraction.text.toString()).isEmpty() + val mathExpressionInteractionsView = + it.findViewById(R.id.math_expression_input_interaction_view) + assertThat(mathExpressionInteractionsView.text.toString()).isEmpty() } } } From 343d46a0c29c599ef78916b0314e0c2b7cccfef9 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sun, 13 Nov 2022 08:12:45 +0530 Subject: [PATCH 69/76] Fixed 100 characters limit --- .../org/oppia/android/app/player/state/StateFragmentTest.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt index 10d66890961..a925eb9dd47 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 @@ -2014,7 +2014,9 @@ class StateFragmentTest { it.onActivity { val mathExpressionInteractionsView = - it.findViewById(R.id.math_expression_input_interaction_view) + it.findViewById( + R.id.math_expression_input_interaction_view + ) assertThat(mathExpressionInteractionsView.text.toString()).isEmpty() } } From 6fcffc92c47eee713044422d8db7349cf8292c89 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sun, 13 Nov 2022 12:18:04 +0530 Subject: [PATCH 70/76] updated test --- .../org/oppia/android/app/player/state/StateFragmentTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/state/StateFragmentTest.kt index a925eb9dd47..9c8b7354f5d 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 @@ -2017,8 +2017,9 @@ class StateFragmentTest { it.findViewById( R.id.math_expression_input_interaction_view ) - assertThat(mathExpressionInteractionsView.text.toString()).isEmpty() + assertThat(mathExpressionInteractionsView.text.toString()).isEqualTo("a") } + onView(withId(R.id.math_expression_input_error)).check(matches(isDisplayed())) } } From a360f59b94ebf77faae3a17bba7bbbb7c6bb0a25 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Sun, 13 Nov 2022 12:59:36 +0530 Subject: [PATCH 71/76] Re-run ci checks From 650fdabc1cd9194c99a0eed4ad94a0c02b4e4cf4 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 14 Nov 2022 02:39:51 +0530 Subject: [PATCH 72/76] Removed animation --- .../player/state/SelectionInteractionView.kt | 34 +++++++++++++++---- .../SelectionInteractionContentViewModel.kt | 1 + .../SelectionInteractionViewModel.kt | 2 ++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt index 1265ed41808..ca91807a969 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt @@ -2,6 +2,7 @@ package org.oppia.android.app.player.state import android.content.Context import android.util.AttributeSet +import android.util.Log import android.view.LayoutInflater import androidx.databinding.ObservableList import androidx.fragment.app.Fragment @@ -18,6 +19,8 @@ import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.ExplorationHtmlParserEntityType import org.oppia.android.util.parser.html.HtmlParser import javax.inject.Inject +import kotlinx.android.synthetic.main.item_selection_interaction_items.view.* +import kotlinx.android.synthetic.main.multiple_choice_interaction_items.view.* /** * A custom [RecyclerView] for displaying a variable list of items that may be selected by a user as @@ -28,12 +31,17 @@ class SelectionInteractionView @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RecyclerView(context, attrs, defStyleAttr) { - @field:[Inject ExplorationHtmlParserEntityType] lateinit var entityType: String - @field:[Inject DefaultResourceBucketName] lateinit var resourceBucketName: String + @field:[Inject ExplorationHtmlParserEntityType] + lateinit var entityType: String + @field:[Inject DefaultResourceBucketName] + lateinit var resourceBucketName: String - @Inject lateinit var htmlParserFactory: HtmlParser.Factory - @Inject lateinit var bindingInterface: ViewBindingShim - @Inject lateinit var singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + @Inject + lateinit var htmlParserFactory: HtmlParser.Factory + @Inject + lateinit var bindingInterface: ViewBindingShim + @Inject + lateinit var singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory private lateinit var selectionItemInputType: SelectionItemInputType private lateinit var entityId: String @@ -99,11 +107,17 @@ class SelectionInteractionView @JvmOverloads constructor( singleTypeBuilderFactory.create() .registerViewBinder( inflateView = { parent -> - bindingInterface.provideSelectionInteractionViewInflatedView( + val checkBox = bindingInterface.provideSelectionInteractionViewInflatedView( LayoutInflater.from(parent.context), parent, /* attachToParent= */ false ) + if (dataList.map { it.disableAnimation }.any { it }) { + checkBox.multiple_choice_radio_button.setOnCheckedChangeListener { buttonView, _ -> + buttonView.jumpDrawablesToCurrentState() + } + } + checkBox }, bindView = { view, viewModel -> bindingInterface.provideSelectionInteractionViewModel( @@ -122,11 +136,17 @@ class SelectionInteractionView @JvmOverloads constructor( singleTypeBuilderFactory.create() .registerViewBinder( inflateView = { parent -> - bindingInterface.provideMultipleChoiceInteractionItemsInflatedView( + val radioButton = bindingInterface.provideMultipleChoiceInteractionItemsInflatedView( LayoutInflater.from(parent.context), parent, /* attachToParent= */ false ) + if (dataList.map { it.disableAnimation }.any { it }) { + radioButton.multiple_choice_radio_button.setOnCheckedChangeListener { buttonView, _ -> + buttonView.jumpDrawablesToCurrentState() + } + } + radioButton }, bindView = { view, viewModel -> bindingInterface.provideMultipleChoiceInteractionItemsViewModel( diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt index e726301dd02..a38002fac2b 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt @@ -8,6 +8,7 @@ import org.oppia.android.app.viewmodel.ObservableViewModel class SelectionInteractionContentViewModel( val htmlContent: SubtitledHtml, val hasConversationView: Boolean, + var disableAnimation: Boolean, private val itemIndex: Int, private val selectionInteractionViewModel: SelectionInteractionViewModel ) : ObservableViewModel() { 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 4474bc57cca..75700179f56 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 @@ -77,6 +77,7 @@ class SelectionInteractionViewModel private constructor( rawUserAnswer.itemSelection.selectedIndexesList.forEach { index -> selectedItems += index updateIsAnswerAvailable() + choiceItems[index].disableAnimation = true choiceItems[index].isAnswerSelected.set(true) } } @@ -218,6 +219,7 @@ class SelectionInteractionViewModel private constructor( SelectionInteractionContentViewModel( htmlContent = subtitledHtml, hasConversationView = hasConversationView, + disableAnimation = false, itemIndex = index, selectionInteractionViewModel = selectionInteractionViewModel ) From 444406c7fa08928c28566bd35428f766d799a228 Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 14 Nov 2022 02:41:27 +0530 Subject: [PATCH 73/76] Optimized code --- .../app/player/state/SelectionInteractionView.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt index ca91807a969..df2a189238c 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt @@ -2,12 +2,13 @@ package org.oppia.android.app.player.state import android.content.Context import android.util.AttributeSet -import android.util.Log import android.view.LayoutInflater import androidx.databinding.ObservableList import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.item_selection_interaction_items.view.* +import kotlinx.android.synthetic.main.multiple_choice_interaction_items.view.* import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.itemviewmodel.SelectionInteractionContentViewModel import org.oppia.android.app.player.state.itemviewmodel.SelectionItemInputType @@ -19,8 +20,6 @@ import org.oppia.android.util.gcsresource.DefaultResourceBucketName import org.oppia.android.util.parser.html.ExplorationHtmlParserEntityType import org.oppia.android.util.parser.html.HtmlParser import javax.inject.Inject -import kotlinx.android.synthetic.main.item_selection_interaction_items.view.* -import kotlinx.android.synthetic.main.multiple_choice_interaction_items.view.* /** * A custom [RecyclerView] for displaying a variable list of items that may be selected by a user as @@ -113,8 +112,8 @@ class SelectionInteractionView @JvmOverloads constructor( /* attachToParent= */ false ) if (dataList.map { it.disableAnimation }.any { it }) { - checkBox.multiple_choice_radio_button.setOnCheckedChangeListener { buttonView, _ -> - buttonView.jumpDrawablesToCurrentState() + checkBox.multiple_choice_radio_button.setOnCheckedChangeListener { view, _ -> + view.jumpDrawablesToCurrentState() } } checkBox @@ -142,8 +141,8 @@ class SelectionInteractionView @JvmOverloads constructor( /* attachToParent= */ false ) if (dataList.map { it.disableAnimation }.any { it }) { - radioButton.multiple_choice_radio_button.setOnCheckedChangeListener { buttonView, _ -> - buttonView.jumpDrawablesToCurrentState() + radioButton.multiple_choice_radio_button.setOnCheckedChangeListener { view, _ -> + view.jumpDrawablesToCurrentState() } } radioButton From 992c479f10f40463cd1b79044389093b4801e2df Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 14 Nov 2022 10:12:30 +0530 Subject: [PATCH 74/76] Reverted back changes for selection interaction --- .../player/state/SelectionInteractionView.kt | 24 ++++--------------- .../SelectionInteractionContentViewModel.kt | 1 - .../SelectionInteractionViewModel.kt | 2 -- 3 files changed, 4 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt index df2a189238c..fb869c2c1f0 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt @@ -7,8 +7,6 @@ import androidx.databinding.ObservableList import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.item_selection_interaction_items.view.* -import kotlinx.android.synthetic.main.multiple_choice_interaction_items.view.* import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.itemviewmodel.SelectionInteractionContentViewModel import org.oppia.android.app.player.state.itemviewmodel.SelectionItemInputType @@ -30,10 +28,8 @@ class SelectionInteractionView @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : RecyclerView(context, attrs, defStyleAttr) { - @field:[Inject ExplorationHtmlParserEntityType] - lateinit var entityType: String - @field:[Inject DefaultResourceBucketName] - lateinit var resourceBucketName: String + @field:[Inject ExplorationHtmlParserEntityType] lateinit var entityType: String + @field:[Inject DefaultResourceBucketName] lateinit var resourceBucketName: String @Inject lateinit var htmlParserFactory: HtmlParser.Factory @@ -106,17 +102,11 @@ class SelectionInteractionView @JvmOverloads constructor( singleTypeBuilderFactory.create() .registerViewBinder( inflateView = { parent -> - val checkBox = bindingInterface.provideSelectionInteractionViewInflatedView( + bindingInterface.provideSelectionInteractionViewInflatedView( LayoutInflater.from(parent.context), parent, /* attachToParent= */ false ) - if (dataList.map { it.disableAnimation }.any { it }) { - checkBox.multiple_choice_radio_button.setOnCheckedChangeListener { view, _ -> - view.jumpDrawablesToCurrentState() - } - } - checkBox }, bindView = { view, viewModel -> bindingInterface.provideSelectionInteractionViewModel( @@ -135,17 +125,11 @@ class SelectionInteractionView @JvmOverloads constructor( singleTypeBuilderFactory.create() .registerViewBinder( inflateView = { parent -> - val radioButton = bindingInterface.provideMultipleChoiceInteractionItemsInflatedView( + bindingInterface.provideMultipleChoiceInteractionItemsInflatedView( LayoutInflater.from(parent.context), parent, /* attachToParent= */ false ) - if (dataList.map { it.disableAnimation }.any { it }) { - radioButton.multiple_choice_radio_button.setOnCheckedChangeListener { view, _ -> - view.jumpDrawablesToCurrentState() - } - } - radioButton }, bindView = { view, viewModel -> bindingInterface.provideMultipleChoiceInteractionItemsViewModel( diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt index a38002fac2b..e726301dd02 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionContentViewModel.kt @@ -8,7 +8,6 @@ import org.oppia.android.app.viewmodel.ObservableViewModel class SelectionInteractionContentViewModel( val htmlContent: SubtitledHtml, val hasConversationView: Boolean, - var disableAnimation: Boolean, private val itemIndex: Int, private val selectionInteractionViewModel: SelectionInteractionViewModel ) : ObservableViewModel() { 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 75700179f56..4474bc57cca 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 @@ -77,7 +77,6 @@ class SelectionInteractionViewModel private constructor( rawUserAnswer.itemSelection.selectedIndexesList.forEach { index -> selectedItems += index updateIsAnswerAvailable() - choiceItems[index].disableAnimation = true choiceItems[index].isAnswerSelected.set(true) } } @@ -219,7 +218,6 @@ class SelectionInteractionViewModel private constructor( SelectionInteractionContentViewModel( htmlContent = subtitledHtml, hasConversationView = hasConversationView, - disableAnimation = false, itemIndex = index, selectionInteractionViewModel = selectionInteractionViewModel ) From 37f59fd1d2a556d9da8ff7e21370b665adca711a Mon Sep 17 00:00:00 2001 From: vrajdesai78 Date: Mon, 14 Nov 2022 12:04:07 +0530 Subject: [PATCH 75/76] added coordinates param --- .../android/app/player/state/SelectionInteractionView.kt | 9 +++------ .../ImageRegionSelectionInteractionViewModel.kt | 3 ++- .../app/testing/ImageRegionSelectionTestFragment.kt | 5 +++-- .../org/oppia/android/app/utility/ClickableAreasImage.kt | 4 +++- .../app/utility/OnClickableAreaClickedListener.kt | 7 ++++++- .../layout/image_region_selection_interaction_item.xml | 2 +- 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt index fb869c2c1f0..1265ed41808 100644 --- a/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt +++ b/app/src/main/java/org/oppia/android/app/player/state/SelectionInteractionView.kt @@ -31,12 +31,9 @@ class SelectionInteractionView @JvmOverloads constructor( @field:[Inject ExplorationHtmlParserEntityType] lateinit var entityType: String @field:[Inject DefaultResourceBucketName] lateinit var resourceBucketName: String - @Inject - lateinit var htmlParserFactory: HtmlParser.Factory - @Inject - lateinit var bindingInterface: ViewBindingShim - @Inject - lateinit var singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory + @Inject lateinit var htmlParserFactory: HtmlParser.Factory + @Inject lateinit var bindingInterface: ViewBindingShim + @Inject lateinit var singleTypeBuilderFactory: BindableAdapter.SingleTypeBuilder.Factory private lateinit var selectionItemInputType: SelectionItemInputType private lateinit var entityId: String 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 b084f76a36d..aca0fbb5213 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 @@ -8,6 +8,7 @@ import org.oppia.android.app.model.ClickOnImage import org.oppia.android.app.model.ImageWithRegions.LabeledRegion import org.oppia.android.app.model.Interaction import org.oppia.android.app.model.InteractionObject +import org.oppia.android.app.model.Point2d import org.oppia.android.app.model.RawUserAnswer import org.oppia.android.app.model.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext @@ -60,7 +61,7 @@ class ImageRegionSelectionInteractionViewModel private constructor( isAnswerAvailable.addOnPropertyChangedCallback(callback) } - override fun onClickableAreaTouched(region: RegionClickedEvent) { + override fun onClickableAreaTouched(region: RegionClickedEvent, coordinates: Point2d) { when (region) { is DefaultRegionClickedEvent -> { answerText = "" diff --git a/app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestFragment.kt b/app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestFragment.kt index 43c0374a19d..4e1c22ec9a9 100644 --- a/app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestFragment.kt +++ b/app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestFragment.kt @@ -7,6 +7,7 @@ import android.view.View import android.view.ViewGroup import org.oppia.android.app.fragment.FragmentComponentImpl import org.oppia.android.app.fragment.InjectableFragment +import org.oppia.android.app.model.Point2d import org.oppia.android.app.utility.ClickableAreasImage import org.oppia.android.app.utility.OnClickableAreaClickedListener import org.oppia.android.app.utility.RegionClickedEvent @@ -36,7 +37,7 @@ class ImageRegionSelectionTestFragment : InjectableFragment(), OnClickableAreaCl return imageRegionSelectionTestFragmentPresenter.handleCreateView(inflater, container) } - override fun onClickableAreaTouched(region: RegionClickedEvent) { - mockOnClickableAreaClickedListener.onClickableAreaTouched(region) + override fun onClickableAreaTouched(region: RegionClickedEvent, coordinates: Point2d) { + mockOnClickableAreaClickedListener.onClickableAreaTouched(region, coordinates) } } diff --git a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt index ad9dabaca99..4c18395e69c 100644 --- a/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt +++ b/app/src/main/java/org/oppia/android/app/utility/ClickableAreasImage.kt @@ -10,6 +10,7 @@ import androidx.core.view.forEachIndexed import androidx.core.view.isVisible import org.oppia.android.R import org.oppia.android.app.model.ImageWithRegions +import org.oppia.android.app.model.Point2d import org.oppia.android.app.player.state.ImageRegionSelectionInteractionView import org.oppia.android.app.shim.ViewBindingShim import kotlin.math.roundToInt @@ -48,7 +49,8 @@ class ClickableAreasImage( defaultRegionView.setBackgroundResource(R.drawable.selected_region_background) defaultRegionView.x = x defaultRegionView.y = y - listener.onClickableAreaTouched(DefaultRegionClickedEvent()) + val coordinates = Point2d.newBuilder().setX(x).setY(y).build() + listener.onClickableAreaTouched(DefaultRegionClickedEvent(), coordinates) } } diff --git a/app/src/main/java/org/oppia/android/app/utility/OnClickableAreaClickedListener.kt b/app/src/main/java/org/oppia/android/app/utility/OnClickableAreaClickedListener.kt index 45909da4882..1141d974036 100644 --- a/app/src/main/java/org/oppia/android/app/utility/OnClickableAreaClickedListener.kt +++ b/app/src/main/java/org/oppia/android/app/utility/OnClickableAreaClickedListener.kt @@ -1,5 +1,7 @@ package org.oppia.android.app.utility +import org.oppia.android.app.model.Point2d + /** Listener for an image when it is clicked which have a [ClickableAreasImage] attached to the view. */ interface OnClickableAreaClickedListener { /** @@ -9,5 +11,8 @@ interface OnClickableAreaClickedListener { * For an unspecified region it will be called with [DefaultRegionClickedEvent]. * */ - fun onClickableAreaTouched(region: RegionClickedEvent) + fun onClickableAreaTouched( + region: RegionClickedEvent, + coordinates: Point2d = Point2d.getDefaultInstance() + ) } diff --git a/app/src/main/res/layout/image_region_selection_interaction_item.xml b/app/src/main/res/layout/image_region_selection_interaction_item.xml index 0f1162aa4da..8eeba0f5548 100644 --- a/app/src/main/res/layout/image_region_selection_interaction_item.xml +++ b/app/src/main/res/layout/image_region_selection_interaction_item.xml @@ -52,7 +52,7 @@ app:entityId="@{viewModel.entityId}" app:imageUrl="@{viewModel.imagePath}" app:lastSelectedRegion="@{viewModel.lastSelectedRegion}" - app:onRegionClicked="@{(region) -> viewModel.onClickableAreaTouched(region)}" + app:onRegionClicked="@{(region, coordinates) -> viewModel.onClickableAreaTouched(region, coordinates)}" app:overlayView="@{interactionContainerFrameLayout}" /> Date: Mon, 14 Nov 2022 12:05:25 +0530 Subject: [PATCH 76/76] Updated Kdoc --- .../oppia/android/app/utility/OnClickableAreaClickedListener.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/oppia/android/app/utility/OnClickableAreaClickedListener.kt b/app/src/main/java/org/oppia/android/app/utility/OnClickableAreaClickedListener.kt index 1141d974036..6c5b399da67 100644 --- a/app/src/main/java/org/oppia/android/app/utility/OnClickableAreaClickedListener.kt +++ b/app/src/main/java/org/oppia/android/app/utility/OnClickableAreaClickedListener.kt @@ -10,6 +10,7 @@ interface OnClickableAreaClickedListener { * For an specified region it will be called with [NamedRegionClickedEvent] with region name. * For an unspecified region it will be called with [DefaultRegionClickedEvent]. * + * @param coordinates the coordinates of unlabelled region */ fun onClickableAreaTouched( region: RegionClickedEvent,