diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd85589b1b9..41d1ce55918 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -224,6 +224,7 @@ + diff --git a/app/src/main/java/org/oppia/android/app/activity/ActivityComponentImpl.kt b/app/src/main/java/org/oppia/android/app/activity/ActivityComponentImpl.kt index a4f6d5a6d5d..12c9b38749e 100644 --- a/app/src/main/java/org/oppia/android/app/activity/ActivityComponentImpl.kt +++ b/app/src/main/java/org/oppia/android/app/activity/ActivityComponentImpl.kt @@ -73,6 +73,7 @@ import org.oppia.android.app.testing.ImageViewBindingAdaptersTestActivity import org.oppia.android.app.testing.InputInteractionViewTestActivity import org.oppia.android.app.testing.ListItemLeadingMarginSpanTestActivity import org.oppia.android.app.testing.MarginBindingAdaptersTestActivity +import org.oppia.android.app.testing.MathExpressionInteractionsViewTestActivity import org.oppia.android.app.testing.NavigationDrawerTestActivity import org.oppia.android.app.testing.PoliciesFragmentTestActivity import org.oppia.android.app.testing.ProfileChooserFragmentTestActivity @@ -152,6 +153,7 @@ interface ActivityComponentImpl : fun inject(imageViewBindingAdaptersTestActivity: ImageViewBindingAdaptersTestActivity) fun inject(inputInteractionViewTestActivity: InputInteractionViewTestActivity) fun inject(textInputInteractionViewTestActivity: TextInputInteractionViewTestActivity) + fun inject(mathExpressionInteractionsViewTestActivity: MathExpressionInteractionsViewTestActivity) fun inject(ratioInputInteractionViewTestActivity: RatioInputInteractionViewTestActivity) fun inject(licenseListActivity: LicenseListActivity) fun inject(licenseTextViewerActivity: LicenseTextViewerActivity) 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 132c988774b..06b6bb3a47c 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,12 +105,18 @@ class MathExpressionInteractionsViewModel private constructor( override fun onPropertyChanged(sender: Observable, propertyId: Int) { errorOrAvailabilityCheckReceiver.onPendingAnswerErrorOrAvailabilityCheck( pendingAnswerError, - answerText.isNotEmpty() + inputAnswerAvailable = true // Allow blank answer submission. ) } } errorMessage.addOnPropertyChangedCallback(callback) isAnswerAvailable.addOnPropertyChangedCallback(callback) + + // Initializing with default values so that submit button is enabled by default. + errorOrAvailabilityCheckReceiver.onPendingAnswerErrorOrAvailabilityCheck( + pendingAnswerError = null, + inputAnswerAvailable = true + ) } override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply { @@ -147,18 +153,16 @@ class MathExpressionInteractionsViewModel private constructor( }.build() 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( - answerText.toString(), allowedVariables, resourceHandler - ) - } + pendingAnswerError = when (category) { + // There's no support for real-time errors. + AnswerErrorCategory.REAL_TIME -> null + AnswerErrorCategory.SUBMIT_TIME -> { + interactionType.computeSubmitTimeError( + answerText.toString(), allowedVariables, resourceHandler + ) } - errorMessage.set(pendingAnswerError) } + errorMessage.set(pendingAnswerError) return pendingAnswerError } @@ -290,7 +294,10 @@ class MathExpressionInteractionsViewModel private constructor( } private companion object { - private enum class InteractionType( + /** + * Enum class representing different types of interactions in a mathematical expression input field. + */ + enum class InteractionType( val viewType: ViewType, @StringRes val defaultHintTextStringId: Int, val hasPlaceholder: Boolean, @@ -420,6 +427,25 @@ class MathExpressionInteractionsViewModel private constructor( allowedVariables: List, appLanguageResourceHandler: AppLanguageResourceHandler ): String? { + if (answerText.isBlank()) { + return when (this) { + NUMERIC_EXPRESSION -> { + appLanguageResourceHandler.getStringInLocale( + R.string.numeric_expression_error_empty_input + ) + } + ALGEBRAIC_EXPRESSION -> { + appLanguageResourceHandler.getStringInLocale( + R.string.algebraic_expression_error_empty_input + ) + } + MATH_EQUATION -> { + appLanguageResourceHandler.getStringInLocale( + R.string.math_equation_error_empty_input + ) + } + } + } return when (val parseResult = parseAnswer(answerText, allowedVariables)) { is MathParsingResult.Failure -> when (val error = parseResult.error) { is DisabledVariablesInUseError -> { 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 86e41201dad..20706aa12a3 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 @@ -10,18 +10,12 @@ import org.oppia.android.app.activity.ActivityComponentImpl import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity import org.oppia.android.app.customview.interaction.NumericInputInteractionView import org.oppia.android.app.model.InputInteractionViewTestActivityParams -import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathInteractionType.ALGEBRAIC_EXPRESSION -import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathInteractionType.MATH_EQUATION -import org.oppia.android.app.model.InputInteractionViewTestActivityParams.MathInteractionType.MATH_INTERACTION_TYPE_UNSPECIFIED -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.UserAnswer import org.oppia.android.app.model.WrittenTranslationContext import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver -import org.oppia.android.app.player.state.itemviewmodel.MathExpressionInteractionsViewModel import org.oppia.android.app.player.state.itemviewmodel.NumericInputViewModel import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.InteractionItemFactory @@ -30,7 +24,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.player.state.itemviewmodel.MathExpressionInteractionsViewModel.FactoryImpl.FactoryFactoryImpl as MathExpViewModelFactoryFactoryImpl /** * This is a dummy activity to test input interaction views. @@ -46,12 +39,8 @@ class InputInteractionViewTestActivity : @Inject lateinit var numericInputViewModelFactory: NumericInputViewModel.FactoryImpl - @Inject - lateinit var mathExpViewModelFactoryFactory: MathExpViewModelFactoryFactoryImpl - val numericInputViewModel by lazy { numericInputViewModelFactory.create() } - lateinit var mathExpressionViewModel: MathExpressionInteractionsViewModel lateinit var writtenTranslationContext: WrittenTranslationContext override fun onCreate(savedInstanceState: Bundle?) { @@ -67,36 +56,8 @@ class InputInteractionViewTestActivity : InputInteractionViewTestActivityParams.getDefaultInstance() ) writtenTranslationContext = params.writtenTranslationContext - when (params.mathInteractionType) { - NUMERIC_EXPRESSION -> { - mathExpressionViewModel = - mathExpViewModelFactoryFactory - .createFactoryForNumericExpression() - .create(interaction = params.interaction) - } - ALGEBRAIC_EXPRESSION -> { - mathExpressionViewModel = - mathExpViewModelFactoryFactory - .createFactoryForAlgebraicExpression() - .create(interaction = params.interaction) - } - MATH_EQUATION -> { - mathExpressionViewModel = - mathExpViewModelFactoryFactory - .createFactoryForMathEquation() - .create(interaction = params.interaction) - } - MATH_INTERACTION_TYPE_UNSPECIFIED, UNRECOGNIZED, null -> { - // Default to numeric expression arbitrarily (since something needs to be defined). - mathExpressionViewModel = - mathExpViewModelFactoryFactory - .createFactoryForNumericExpression() - .create(interaction = params.interaction) - } - } binding.numericInputViewModel = numericInputViewModel - binding.mathExpressionInteractionsViewModel = mathExpressionViewModel } fun getPendingAnswerErrorOnSubmitClick(v: View) { diff --git a/app/src/main/java/org/oppia/android/app/testing/MathExpressionInteractionsViewTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/MathExpressionInteractionsViewTestActivity.kt new file mode 100644 index 00000000000..55219840ea9 --- /dev/null +++ b/app/src/main/java/org/oppia/android/app/testing/MathExpressionInteractionsViewTestActivity.kt @@ -0,0 +1,147 @@ +package org.oppia.android.app.testing + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.databinding.DataBindingUtil +import org.oppia.android.R +import org.oppia.android.app.activity.ActivityComponentImpl +import org.oppia.android.app.activity.InjectableAutoLocalizedAppCompatActivity +import org.oppia.android.app.model.Interaction +import org.oppia.android.app.model.MathExpressionInteractionsViewTestActivityParams +import org.oppia.android.app.model.MathExpressionInteractionsViewTestActivityParams.MathInteractionType +import org.oppia.android.app.model.UserAnswer +import org.oppia.android.app.model.WrittenTranslationContext +import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory +import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver +import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver +import org.oppia.android.app.player.state.itemviewmodel.MathExpressionInteractionsViewModel +import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel +import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener +import org.oppia.android.databinding.ActivityMathExpressionInteractionViewTestBinding +import org.oppia.android.util.extensions.getProtoExtra +import org.oppia.android.util.extensions.putProtoExtra +import javax.inject.Inject + +/** + * This is a dummy activity to test input interaction views. + * It contains [MathExpressionInteractionsView]. + */ +class MathExpressionInteractionsViewTestActivity : + InjectableAutoLocalizedAppCompatActivity(), + StateKeyboardButtonListener, + InteractionAnswerErrorOrAvailabilityCheckReceiver, + InteractionAnswerReceiver { + + private lateinit var binding: + ActivityMathExpressionInteractionViewTestBinding + + /** + * Injects the [MathExpressionInteractionsViewModel.FactoryImpl] for creating + * [MathExpressionInteractionsViewModel] instances. + */ + @Inject + lateinit var mathExpViewModelFactoryFactory: + MathExpressionInteractionsViewModel.FactoryImpl.FactoryFactoryImpl + + /** The [MathExpressionInteractionsViewModel] instance. */ + lateinit var mathExpressionViewModel: MathExpressionInteractionsViewModel + + /** Gives access to the translation context. */ + lateinit var writtenTranslationContext: WrittenTranslationContext + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + (activityComponent as ActivityComponentImpl).inject(this) + binding = DataBindingUtil.setContentView( + this, R.layout.activity_math_expression_interaction_view_test + ) + val params = + intent.getProtoExtra( + TEST_ACTIVITY_PARAMS_ARGUMENT_KEY, + MathExpressionInteractionsViewTestActivityParams.getDefaultInstance() + ) + writtenTranslationContext = params.writtenTranslationContext + when (params.mathInteractionType) { + MathInteractionType.NUMERIC_EXPRESSION -> { + mathExpressionViewModel = + mathExpViewModelFactoryFactory + .createFactoryForNumericExpression() + .create(interaction = params.interaction) + } + MathInteractionType.ALGEBRAIC_EXPRESSION -> { + mathExpressionViewModel = + mathExpViewModelFactoryFactory + .createFactoryForAlgebraicExpression() + .create(interaction = params.interaction) + } + MathInteractionType.MATH_EQUATION -> { + mathExpressionViewModel = + mathExpViewModelFactoryFactory + .createFactoryForMathEquation() + .create(interaction = params.interaction) + } + MathInteractionType.MATH_INTERACTION_TYPE_UNSPECIFIED, + MathInteractionType.UNRECOGNIZED, null -> { + // Default to numeric expression arbitrarily (since something needs to be defined). + mathExpressionViewModel = + mathExpViewModelFactoryFactory + .createFactoryForNumericExpression() + .create(interaction = params.interaction) + } + } + + binding.mathExpressionInteractionsViewModel = mathExpressionViewModel + } + + /** Checks submit-time errors. */ + fun getPendingAnswerErrorOnSubmitClick(v: View) { + mathExpressionViewModel.checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME) + } + + override fun onPendingAnswerErrorOrAvailabilityCheck( + pendingAnswerError: String?, + inputAnswerAvailable: Boolean + ) { + binding.submitButton.isEnabled = pendingAnswerError == null + } + + override fun onAnswerReadyForSubmission(answer: UserAnswer) { + } + + override fun onEditorAction(actionCode: Int) { + } + + private inline fun StateItemViewModel + .InteractionItemFactory.create( + interaction: Interaction = Interaction.getDefaultInstance() + ): T { + return create( + entityId = "fake_entity_id", + hasConversationView = false, + interaction = interaction, + interactionAnswerReceiver = this@MathExpressionInteractionsViewTestActivity, + answerErrorReceiver = this@MathExpressionInteractionsViewTestActivity, + hasPreviousButton = false, + isSplitView = false, + writtenTranslationContext, + timeToStartNoticeAnimationMs = null + ) as T + } + + companion object { + private const val TEST_ACTIVITY_PARAMS_ARGUMENT_KEY = + "MathExpressionInteractionsViewTestActivity.params" + + /** Function to create intent for MathExpressionInteractionsViewTestActivity. */ + fun createIntent( + context: Context, + extras: MathExpressionInteractionsViewTestActivityParams + ): Intent { + return Intent(context, MathExpressionInteractionsViewTestActivity::class.java).also { + it.putProtoExtra(TEST_ACTIVITY_PARAMS_ARGUMENT_KEY, extras) + } + } + } +} diff --git a/app/src/main/res/layout/activity_input_interaction_view_test.xml b/app/src/main/res/layout/activity_input_interaction_view_test.xml index 9a7a8b277f3..c60eb2fec5e 100644 --- a/app/src/main/res/layout/activity_input_interaction_view_test.xml +++ b/app/src/main/res/layout/activity_input_interaction_view_test.xml @@ -11,9 +11,6 @@ name="numericInputViewModel" type="org.oppia.android.app.player.state.itemviewmodel.NumericInputViewModel" /> - - -