Skip to content

Commit

Permalink
Merge branch 'develop' into fix-survey-gating
Browse files Browse the repository at this point in the history
  • Loading branch information
adhiamboperes authored Apr 5, 2024
2 parents 35e36e9 + f0e9ac9 commit a5d207f
Show file tree
Hide file tree
Showing 86 changed files with 3,259 additions and 1,092 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/static_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh issue list --limit 2000 --repo oppia/oppia-android --json number > $(pwd)/open_issues.json
bazel run //scripts:todo_open_check -- $(pwd) scripts/assets/todo_open_exemptions.pb open_issues.json
bazel run //scripts:todo_open_check -- $(pwd) scripts/assets/todo_open_exemptions.pb
- name: String Resource Validation Check
if: always()
Expand Down
6 changes: 0 additions & 6 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,6 @@ load("@tools_android//tools/googleservices:defs.bzl", "google_services_workspace

google_services_workspace_dependencies()

git_repository(
name = "circularimageview",
commit = "35d08ba88a4a22e6e9ac96bdc5a68be27b55d09f",
remote = "https://github.com/oppia/CircularImageview",
)

# A custom version of Android SVG is needed since custom changes needed to be added to the library
# to correctly size in-line SVGs (such as those needed for LaTeX-based math expressions).
git_repository(
Expand Down
7 changes: 1 addition & 6 deletions app/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ VIEW_MODELS_WITH_RESOURCE_IMPORTS = [
"src/main/java/org/oppia/android/app/parser/StringToNumberParser.kt",
"src/main/java/org/oppia/android/app/parser/StringToRatioParser.kt",
"src/main/java/org/oppia/android/app/player/audio/AudioViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragDropInteractionContentViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/ImageRegionSelectionInteractionViewModel.kt",
Expand Down Expand Up @@ -320,7 +321,6 @@ VIEW_MODELS = [
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/ContentViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/ContinueInteractionViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/ContinueNavigationButtonViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/DragAndDropSortInteractionViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/FeedbackViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/NextButtonViewModel.kt",
"src/main/java/org/oppia/android/app/player/state/itemviewmodel/NumericInputViewModel.kt",
Expand Down Expand Up @@ -612,10 +612,8 @@ android_library(
"//third_party:androidx_recyclerview_recyclerview",
"//third_party:androidx_viewpager2_viewpager2",
"//third_party:androidx_viewpager_viewpager",
"//third_party:circularimageview_circular_image_view",
"//third_party:com_google_android_flexbox_flexbox",
"//third_party:com_google_android_material_material",
"//third_party:de_hdodenhof_circleimageview",
"//third_party:javax_annotation_javax_annotation-api_jar",
"//third_party:nl_dionsegijn_konfetti",
"//utility/src/main/java/org/oppia/android/util/system:oppia_clock",
Expand Down Expand Up @@ -660,7 +658,6 @@ kt_android_library(
"//third_party:androidx_core_core-ktx",
"//third_party:androidx_databinding_databinding-common",
"//third_party:androidx_databinding_databinding-runtime",
"//third_party:circularimageview_circular_image_view",
"//utility/src/main/java/org/oppia/android/util/accessibility",
"//utility/src/main/java/org/oppia/android/util/parser/html:html_parser",
"//utility/src/main/java/org/oppia/android/util/parser/image:image_loader",
Expand Down Expand Up @@ -747,9 +744,7 @@ android_library(
"//third_party:androidx_constraintlayout_constraintlayout",
"//third_party:androidx_lifecycle_lifecycle-livedata-core",
"//third_party:androidx_lifecycle_lifecycle-livedata-ktx",
"//third_party:circularimageview_circular_image_view",
"//third_party:com_google_android_material_material",
"//third_party:de_hdodenhof_circleimageview",
"//utility",
"//utility/src/main/java/org/oppia/android/util/system:oppia_clock",
"//utility/src/main/java/org/oppia/android/util/system:oppia_clock_injector_provider",
Expand Down
2 changes: 0 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,6 @@ dependencies {
'com.google.firebase:firebase-auth-ktx:19.3.1',
'com.google.guava:guava:28.1-android',
'com.google.protobuf:protobuf-javalite:3.17.3',
'com.github.oppia:CircularImageview:35d08ba88a',
'de.hdodenhof:circleimageview:3.0.1',
'nl.dionsegijn:konfetti:1.2.5',
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version",
'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1',
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@
<activity android:name=".app.testing.InputInteractionViewTestActivity" />
<activity android:name=".app.testing.RatioInputInteractionViewTestActivity" />
<activity android:name=".app.testing.ImageRegionSelectionTestActivity" />
<activity android:name=".app.testing.MathExpressionInteractionsViewTestActivity" />
<activity android:name=".app.testing.ImageViewBindingAdaptersTestActivity" />
<activity android:name=".app.testing.ListItemLeadingMarginSpanTestActivity" />
<activity android:name=".app.testing.MarginBindingAdaptersTestActivity" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.oppia.android.app.player.state.itemviewmodel

import androidx.annotation.StringRes
import androidx.databinding.Observable
import androidx.databinding.ObservableField
import androidx.recyclerview.widget.RecyclerView
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.ListOfSetsOfHtmlStrings
Expand All @@ -13,6 +15,7 @@ import org.oppia.android.app.model.SubtitledHtml
import org.oppia.android.app.model.TranslatableHtmlContentId
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
Expand All @@ -23,6 +26,18 @@ import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.domain.translation.TranslationController
import javax.inject.Inject

/** Represents the type of errors that can be thrown by drag and drop sort interaction. */
enum class DragAndDropSortInteractionError(@StringRes private var error: Int?) {
VALID(error = null),
EMPTY_INPUT(error = R.string.drag_and_drop_interaction_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 drag drop & sort choice list. */
class DragAndDropSortInteractionViewModel private constructor(
val entityId: String,
Expand Down Expand Up @@ -55,25 +70,34 @@ class DragAndDropSortInteractionViewModel private constructor(
subtitledHtml.contentId to translatedHtml
}

private val _choiceItems: MutableList<DragDropInteractionContentViewModel> =
private val _originalChoiceItems: MutableList<DragDropInteractionContentViewModel> =
computeChoiceItems(contentIdHtmlMap, choiceSubtitledHtmls, this, resourceHandler)

private val _choiceItems = _originalChoiceItems.toMutableList()
val choiceItems: List<DragDropInteractionContentViewModel> = _choiceItems

private var pendingAnswerError: String? = null
private val isAnswerAvailable = ObservableField(false)
var errorMessage = ObservableField<String>("")

init {
val callback: Observable.OnPropertyChangedCallback =
object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
interactionAnswerErrorOrAvailabilityCheckReceiver.onPendingAnswerErrorOrAvailabilityCheck(
pendingAnswerError = null,
inputAnswerAvailable = true
pendingAnswerError,
inputAnswerAvailable = true // Allow submission without arranging or merging items.
)
}
}
isAnswerAvailable.addOnPropertyChangedCallback(callback)
isAnswerAvailable.set(true) // For drag drop submit button will be enabled by default.
errorMessage.addOnPropertyChangedCallback(callback)

// Initializing with default values so that submit button is enabled by default.
interactionAnswerErrorOrAvailabilityCheckReceiver.onPendingAnswerErrorOrAvailabilityCheck(
pendingAnswerError = null,
inputAnswerAvailable = true
)
}

override fun onItemDragged(
Expand All @@ -98,6 +122,7 @@ class DragAndDropSortInteractionViewModel private constructor(
if (allowMultipleItemsInSamePosition) {
(adapter as BindableAdapter<*>).setDataUnchecked(_choiceItems)
}
checkPendingAnswerError(AnswerErrorCategory.REAL_TIME)
}

fun onItemMoved(
Expand Down Expand Up @@ -129,6 +154,20 @@ class DragAndDropSortInteractionViewModel private constructor(
this@DragAndDropSortInteractionViewModel.writtenTranslationContext
}.build()

/**
* It checks the pending error for the current drag and drop sort interaction, 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 convertItemsToAnswer(htmlItems: List<StringList>): ListOfSetsOfHtmlStrings {
return ListOfSetsOfHtmlStrings.newBuilder()
Expand Down Expand Up @@ -190,6 +229,13 @@ class DragAndDropSortInteractionViewModel private constructor(
(adapter as BindableAdapter<*>).setDataUnchecked(_choiceItems)
}

private fun getSubmitTimeError(): DragAndDropSortInteractionError {
return if (_originalChoiceItems == _choiceItems)
DragAndDropSortInteractionError.EMPTY_INPUT
else
DragAndDropSortInteractionError.VALID
}

/** Implementation of [StateItemViewModel.InteractionItemFactory] for this view model. */
class FactoryImpl @Inject constructor(
private val resourceHandler: AppLanguageResourceHandler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -420,6 +427,25 @@ class MathExpressionInteractionsViewModel private constructor(
allowedVariables: List<String>,
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 -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -46,12 +39,8 @@ class InputInteractionViewTestActivity :
@Inject
lateinit var numericInputViewModelFactory: NumericInputViewModel.FactoryImpl

@Inject
lateinit var mathExpViewModelFactoryFactory: MathExpViewModelFactoryFactoryImpl

val numericInputViewModel by lazy { numericInputViewModelFactory.create<NumericInputViewModel>() }

lateinit var mathExpressionViewModel: MathExpressionInteractionsViewModel
lateinit var writtenTranslationContext: WrittenTranslationContext

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -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) {
Expand Down
Loading

0 comments on commit a5d207f

Please sign in to comment.