From 676cfb958cbee40f4ace97fddf8dd46a5860782c Mon Sep 17 00:00:00 2001 From: Saptak Manna Date: Thu, 18 Jan 2024 21:15:54 +0530 Subject: [PATCH] Add empty answer support --- .../SelectionInteractionViewModel.kt | 49 ++++++++++++++++++- .../res/layout/selection_interaction_item.xml | 10 ++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/SelectionInteractionViewModel.kt index 796df10ec86..6d59a321fe4 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 androidx.annotation.StringRes import androidx.databinding.Observable import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableField @@ -18,6 +19,7 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiv import org.oppia.android.app.translation.AppLanguageResourceHandler import org.oppia.android.app.viewmodel.ObservableArrayList import org.oppia.android.domain.translation.TranslationController +import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import javax.inject.Inject /** Corresponds to the type of input that should be used for an item selection interaction view. */ @@ -26,6 +28,18 @@ enum class SelectionItemInputType { RADIO_BUTTONS } +/** Enum to the store the errors of selection input */ +enum class SelectionInputError(@StringRes private var error: Int?) { + VALID(error = null), + EMPTY_INPUT(error = R.string.selection_error_empty_input); + + /** + * Returns the string corresponding to this error's string resources, or null if there is none. + */ + fun getErrorMessageFromStringRes(resourceHandler: AppLanguageResourceHandler): String? = + error?.let(resourceHandler::getStringInLocale) +} + /** [StateItemViewModel] for multiple or item-selection input choice list. */ class SelectionInteractionViewModel private constructor( val entityId: String, @@ -64,7 +78,9 @@ class SelectionInteractionViewModel private constructor( val choiceItems: ObservableList = computeChoiceItems(choiceSubtitledHtmls, hasConversationView, this, enabledItemsList) + private var pendingAnswerError: String? = null private val isAnswerAvailable = ObservableField(false) + var errorMessage = ObservableField("") val selectedItemText = ObservableField( resourceHandler.getStringInLocale( @@ -77,12 +93,19 @@ class SelectionInteractionViewModel private constructor( object : Observable.OnPropertyChangedCallback() { override fun onPropertyChanged(sender: Observable, propertyId: Int) { interactionAnswerErrorOrAvailabilityCheckReceiver.onPendingAnswerErrorOrAvailabilityCheck( - pendingAnswerError = null, - inputAnswerAvailable = selectedItems.isNotEmpty() + pendingAnswerError, + inputAnswerAvailable = true // Allow blank answer submission. ) } } + errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) + + // Initializing with default values so that submit button is enabled by default. + interactionAnswerErrorOrAvailabilityCheckReceiver.onPendingAnswerErrorOrAvailabilityCheck( + pendingAnswerError = null, + inputAnswerAvailable = true + ) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -113,6 +136,20 @@ class SelectionInteractionViewModel private constructor( writtenTranslationContext = translationContext }.build() + /** + * It checks the pending error for the current selection input, and correspondingly + * updates the error string based on the specified error category. + */ + override fun checkPendingAnswerError(category: AnswerErrorCategory): String? { + pendingAnswerError = when (category) { + AnswerErrorCategory.REAL_TIME -> null + AnswerErrorCategory.SUBMIT_TIME -> + getSubmitTimeError().getErrorMessageFromStringRes(resourceHandler) + } + errorMessage.set(pendingAnswerError) + return pendingAnswerError + } + /** 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) { @@ -135,6 +172,7 @@ class SelectionInteractionViewModel private constructor( /** Catalogs an item being clicked by the user and returns whether the item should be considered selected. */ fun updateSelection(itemIndex: Int, isCurrentlySelected: Boolean): Boolean { + checkPendingAnswerError(AnswerErrorCategory.REAL_TIME) return when { isCurrentlySelected -> { selectedItems -= itemIndex @@ -208,6 +246,13 @@ class SelectionInteractionViewModel private constructor( } } + private fun getSubmitTimeError(): SelectionInputError { + return if (selectedItems.isEmpty()) + SelectionInputError.EMPTY_INPUT + else + SelectionInputError.VALID + } + /** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */ class FactoryImpl @Inject constructor( private val translationController: TranslationController, diff --git a/app/src/main/res/layout/selection_interaction_item.xml b/app/src/main/res/layout/selection_interaction_item.xml index d83395e7f1b..e192fe7af02 100644 --- a/app/src/main/res/layout/selection_interaction_item.xml +++ b/app/src/main/res/layout/selection_interaction_item.xml @@ -60,5 +60,15 @@ app:selectionData="@{viewModel.choiceItems}" app:writtenTranslationContext="@{viewModel.writtenTranslationContext}" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cc2b39169e5..2c0c0c595a6 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -176,6 +176,7 @@ Number of terms is not equal to the required terms. Ratios cannot have 0 as an element. Enter a ratio to continue. + Choose an answer to continue. Unknown size %s Bytes %s KB