diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 76102786288..820522d91e7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -221,6 +221,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 14645028aa5..53f0f35955f 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
@@ -77,6 +77,7 @@ import org.oppia.android.app.testing.NavigationDrawerTestActivity
import org.oppia.android.app.testing.PoliciesFragmentTestActivity
import org.oppia.android.app.testing.ProfileChooserFragmentTestActivity
import org.oppia.android.app.testing.ProfileEditFragmentTestActivity
+import org.oppia.android.app.testing.RatioInputInteractionViewTestActivity
import org.oppia.android.app.testing.SplashTestActivity
import org.oppia.android.app.testing.SpotlightFragmentTestActivity
import org.oppia.android.app.testing.StateAssemblerMarginBindingAdaptersTestActivity
@@ -149,6 +150,7 @@ interface ActivityComponentImpl :
fun inject(imageRegionSelectionTestActivity: ImageRegionSelectionTestActivity)
fun inject(imageViewBindingAdaptersTestActivity: ImageViewBindingAdaptersTestActivity)
fun inject(inputInteractionViewTestActivity: InputInteractionViewTestActivity)
+ fun inject(ratioInputInteractionViewTestActivity: RatioInputInteractionViewTestActivity)
fun inject(licenseListActivity: LicenseListActivity)
fun inject(licenseTextViewerActivity: LicenseTextViewerActivity)
fun inject(listItemLeadingMarginSpanTestActivity: ListItemLeadingMarginSpanTestActivity)
diff --git a/app/src/main/java/org/oppia/android/app/parser/StringToRatioParser.kt b/app/src/main/java/org/oppia/android/app/parser/StringToRatioParser.kt
index 31895402263..ebb62121372 100644
--- a/app/src/main/java/org/oppia/android/app/parser/StringToRatioParser.kt
+++ b/app/src/main/java/org/oppia/android/app/parser/StringToRatioParser.kt
@@ -29,6 +29,7 @@ class StringToRatioParser {
val normalized = text.normalizeWhitespace()
val ratio = parseRatioOrNull(normalized)
return when {
+ normalized.isBlank() -> RatioParsingError.EMPTY_INPUT
!normalized.matches(invalidRatioRegex) || ratio == null -> RatioParsingError.INVALID_FORMAT
numberOfTerms != 0 && ratio.ratioComponentCount != numberOfTerms -> {
RatioParsingError.INVALID_SIZE
@@ -77,7 +78,8 @@ class StringToRatioParser {
INVALID_FORMAT(error = R.string.ratio_error_invalid_format),
INVALID_COLONS(error = R.string.ratio_error_invalid_colons),
INVALID_SIZE(error = R.string.ratio_error_invalid_size),
- INCLUDES_ZERO(error = R.string.ratio_error_includes_zero);
+ INCLUDES_ZERO(error = R.string.ratio_error_includes_zero),
+ EMPTY_INPUT(error = R.string.ratio_error_empty_input);
/**
* Returns the string corresponding to this error's string resources, or null if there is none.
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 49f64619702..f5c0f323bec 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
@@ -46,12 +46,18 @@ class RatioExpressionInputInteractionViewModel 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 {
@@ -67,23 +73,24 @@ class RatioExpressionInputInteractionViewModel private constructor(
}
}.build()
- /** It checks the pending error for the current ratio input, and correspondingly updates the error string based on the specified error category. */
+ /**
+ * 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)
- }
- errorMessage.set(pendingAnswerError)
+ pendingAnswerError = when (category) {
+ AnswerErrorCategory.REAL_TIME ->
+ if (answerText.isNotEmpty())
+ stringToRatioParser.getRealTimeAnswerError(answerText.toString())
+ .getErrorMessageFromStringRes(resourceHandler)
+ else null
+ AnswerErrorCategory.SUBMIT_TIME ->
+ stringToRatioParser.getSubmitTimeError(
+ answerText.toString(),
+ numberOfTerms = numberOfTerms
+ ).getErrorMessageFromStringRes(resourceHandler)
}
+ errorMessage.set(pendingAnswerError)
return pendingAnswerError
}
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 ac786bb146a..f033c023d61 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
@@ -17,7 +17,6 @@ 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.SchemaObject
import org.oppia.android.app.model.UserAnswer
import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory
@@ -25,7 +24,6 @@ import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorO
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.RatioExpressionInputInteractionViewModel
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.InteractionItemFactory
import org.oppia.android.app.player.state.itemviewmodel.TextInputViewModel
@@ -53,9 +51,6 @@ class InputInteractionViewTestActivity :
@Inject
lateinit var textInputViewModelFactory: TextInputViewModel.FactoryImpl
- @Inject
- lateinit var ratioViewModelFactory: RatioExpressionInputInteractionViewModel.FactoryImpl
-
@Inject
lateinit var mathExpViewModelFactoryFactory: MathExpViewModelFactoryFactoryImpl
@@ -63,15 +58,6 @@ class InputInteractionViewTestActivity :
val textInputViewModel by lazy { textInputViewModelFactory.create() }
- val ratioExpressionInputInteractionViewModel by lazy {
- ratioViewModelFactory.create(
- interaction = Interaction.newBuilder().putCustomizationArgs(
- "numberOfTerms",
- SchemaObject.newBuilder().setSignedInt(3).build()
- ).build()
- )
- }
-
lateinit var mathExpressionViewModel: MathExpressionInteractionsViewModel
lateinit var writtenTranslationContext: WrittenTranslationContext
@@ -118,14 +104,11 @@ class InputInteractionViewTestActivity :
binding.numericInputViewModel = numericInputViewModel
binding.textInputViewModel = textInputViewModel
- binding.ratioInteractionInputViewModel = ratioExpressionInputInteractionViewModel
binding.mathExpressionInteractionsViewModel = mathExpressionViewModel
}
fun getPendingAnswerErrorOnSubmitClick(v: View) {
numericInputViewModel.checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME)
- ratioExpressionInputInteractionViewModel
- .checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME)
}
override fun onPendingAnswerErrorOrAvailabilityCheck(
diff --git a/app/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity.kt
new file mode 100644
index 00000000000..a16d8e7c12c
--- /dev/null
+++ b/app/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity.kt
@@ -0,0 +1,120 @@
+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.customview.interaction.RatioInputInteractionView
+import org.oppia.android.app.model.InputInteractionViewTestActivityParams
+import org.oppia.android.app.model.Interaction
+import org.oppia.android.app.model.SchemaObject
+import org.oppia.android.app.model.UserAnswer
+import org.oppia.android.app.model.WrittenTranslationContext
+import org.oppia.android.app.player.state.answerhandling.AnswerErrorCategory
+import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver
+import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver
+import org.oppia.android.app.player.state.itemviewmodel.RatioExpressionInputInteractionViewModel
+import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel
+import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.InteractionItemFactory
+import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener
+import org.oppia.android.databinding.ActivityRatioInputInteractionViewTestBinding
+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 [RatioInputInteractionView].
+ */
+class RatioInputInteractionViewTestActivity :
+ InjectableAutoLocalizedAppCompatActivity(),
+ StateKeyboardButtonListener,
+ InteractionAnswerErrorOrAvailabilityCheckReceiver,
+ InteractionAnswerReceiver {
+ private lateinit var binding: ActivityRatioInputInteractionViewTestBinding
+
+ @Inject
+ lateinit var ratioViewModelFactory: RatioExpressionInputInteractionViewModel.FactoryImpl
+
+ /**
+ * Gives access to the [RatioExpressionInputInteractionViewModel].
+ */
+ val ratioExpressionInputInteractionViewModel by lazy {
+ ratioViewModelFactory.create(
+ interaction = Interaction.newBuilder().putCustomizationArgs(
+ "numberOfTerms",
+ SchemaObject.newBuilder().setSignedInt(3).build()
+ ).build()
+ )
+ }
+
+ /**
+ * 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_ratio_input_interaction_view_test
+ )
+
+ val params =
+ intent.getProtoExtra(
+ TEST_ACTIVITY_PARAMS_ARGUMENT_KEY,
+ InputInteractionViewTestActivityParams.getDefaultInstance()
+ )
+ writtenTranslationContext = params.writtenTranslationContext
+
+ binding.ratioInteractionInputViewModel = ratioExpressionInputInteractionViewModel
+ }
+
+ /**
+ * Checks for submit time errors.
+ */
+ fun getPendingAnswerErrorOnSubmitClick(v: View) {
+ ratioExpressionInputInteractionViewModel
+ .checkPendingAnswerError(AnswerErrorCategory.SUBMIT_TIME)
+ }
+
+ override fun onAnswerReadyForSubmission(answer: UserAnswer) { }
+
+ override fun onEditorAction(actionCode: Int) { }
+
+ private inline fun InteractionItemFactory.create(
+ interaction: Interaction = Interaction.getDefaultInstance()
+ ): T {
+ return create(
+ entityId = "fake_entity_id",
+ hasConversationView = false,
+ interaction = interaction,
+ interactionAnswerReceiver = this@RatioInputInteractionViewTestActivity,
+ answerErrorReceiver = this@RatioInputInteractionViewTestActivity,
+ hasPreviousButton = false,
+ isSplitView = false,
+ writtenTranslationContext,
+ timeToStartNoticeAnimationMs = null
+ ) as T
+ }
+
+ companion object {
+ private const val TEST_ACTIVITY_PARAMS_ARGUMENT_KEY =
+ "RatioInputInteractionViewTestActivity.params"
+
+ /**
+ * Creates an intent to open [RatioInputInteractionViewTestActivity].
+ */
+ fun createIntent(
+ context: Context,
+ extras: InputInteractionViewTestActivityParams
+ ): Intent {
+ return Intent(context, RatioInputInteractionViewTestActivity::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 a8e798252c0..780294fa6b4 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
@@ -15,10 +15,6 @@
name="textInputViewModel"
type="org.oppia.android.app.player.state.itemviewmodel.TextInputViewModel" />
-
-
@@ -36,43 +32,6 @@
android:orientation="vertical"
tools:context=".testing.InputInteractionViewTestActivity">
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d1250813c09..cc2b39169e5 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -175,6 +175,7 @@
Your answer has two colons (:) next to each other.
Number of terms is not equal to the required terms.
Ratios cannot have 0 as an element.
+ Enter a ratio to continue.
Unknown size
%s Bytes
%s KB
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt
index 20692243b6c..0bd03beb6ae 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/InputInteractionViewTestActivityTest.kt
@@ -2,26 +2,20 @@ package org.oppia.android.app.testing
import android.app.Application
import android.content.res.Configuration
-import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.closeSoftKeyboard
import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.UiController
-import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.scrollTo
import androidx.test.espresso.assertion.ViewAssertions.matches
-import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import dagger.Component
-import org.hamcrest.CoreMatchers.allOf
-import org.hamcrest.Matcher
import org.junit.After
import org.junit.Before
import org.junit.Ignore
@@ -38,7 +32,6 @@ 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.RatioInputInteractionView
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.model.InteractionObject
@@ -462,254 +455,11 @@ class InputInteractionViewTestActivityTest {
.check(matches(withText("abc")))
}
- @Test
- fun testRatioInput_withNoInput_hasCorrectPendingAnswerType() {
- val activityScenario = ActivityScenario.launch(
- InputInteractionViewTestActivity::class.java
- )
- activityScenario.onActivity { activity ->
- val pendingAnswer = activity.ratioExpressionInputInteractionViewModel.getPendingAnswer()
- assertThat(pendingAnswer.answer).isInstanceOf(InteractionObject::class.java)
- assertThat(pendingAnswer.answer.ratioExpression.ratioComponentCount).isEqualTo(0)
- }
- }
-
- @Test
- fun testRatioInput_withRatioOfNumber_hasCorrectPendingAnswer() {
- val activityScenario = ActivityScenario.launch(
- InputInteractionViewTestActivity::class.java
- )
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- setTextToRatioInputInteractionView(
- "1:2:3"
- )
- )
- activityScenario.onActivity { activity ->
- val pendingAnswer = activity.ratioExpressionInputInteractionViewModel.getPendingAnswer()
- assertThat(pendingAnswer.answer).isInstanceOf(InteractionObject::class.java)
- assertThat(pendingAnswer.answer.objectTypeCase).isEqualTo(
- InteractionObject.ObjectTypeCase.RATIO_EXPRESSION
- )
- assertThat(pendingAnswer.answer.ratioExpression.ratioComponentList)
- .isEqualTo(listOf(1, 2, 3))
- }
- }
-
- @Test
- @Ignore("Landscape not properly supported") // TODO(#56): Reenable once landscape is supported.
- fun testRatioInput_withRatio_configChange_hasCorrectPendingAnswer() {
- val activityScenario = ActivityScenario.launch(
- InputInteractionViewTestActivity::class.java
- )
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- editTextInputAction.appendText(
- "1:2"
- )
- )
- activityScenario.onActivity { activity ->
- activity.requestedOrientation = Configuration.ORIENTATION_LANDSCAPE
- }
- onView(withId(R.id.test_ratio_input_interaction_view)).check(matches(isDisplayed()))
- .check(matches(withText("1:2")))
- }
-
- @Test
- fun testRatioInput_withTwoColonsTogether_colonsTogetherFormatErrorIsDisplayed() {
- ActivityScenario.launch(InputInteractionViewTestActivity::class.java).use {
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- setTextToRatioInputInteractionView(
- "1::2"
- )
- )
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.ratio_input_error))
- .check(
- matches(
- withText(
- R.string.ratio_error_invalid_colons
- )
- )
- )
- }
- }
-
- @Test
- fun testRatioInput_withNegativeRatioOfNumber_numberFormatErrorIsDisplayed() {
- ActivityScenario.launch(InputInteractionViewTestActivity::class.java).use {
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- setTextToRatioInputInteractionView(
- "-1:2:3:4"
- )
- )
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.ratio_input_error))
- .check(
- matches(
- withText(
- R.string.ratio_error_invalid_chars
- )
- )
- )
- }
- }
-
- @Test
- @DisableAccessibilityChecks // Disabled, as InputInteractionViewTestActivity is a test file and
- // will not be used by user
- fun testRatioInput_withZeroRatio_submit_numberWithZerosErrorIsDisplayed() {
- ActivityScenario.launch(InputInteractionViewTestActivity::class.java).use {
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- setTextToRatioInputInteractionView(
- "1:0:4"
- )
- )
- testCoroutineDispatchers.runCurrent()
- scrollToSubmitButton()
- onView(withId(R.id.submit_button)).check(matches(isDisplayed())).perform(click())
- onView(withId(R.id.ratio_input_error))
- .check(
- matches(
- withText(
- R.string.ratio_error_includes_zero
- )
- )
- )
- }
- }
-
- @Test
- @DisableAccessibilityChecks // Disabled, as InputInteractionViewTestActivity is a test file and
- // will not be used by user
- fun testRatioInput_withInvalidRatio_submit_numberFormatErrorIsDisplayed() {
- ActivityScenario.launch(InputInteractionViewTestActivity::class.java).use {
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- setTextToRatioInputInteractionView(
- "1: 1 2 :4"
- )
- )
- closeSoftKeyboard()
- testCoroutineDispatchers.runCurrent()
- scrollToSubmitButton()
- onView(withId(R.id.submit_button)).check(matches(isDisplayed())).perform(click())
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.ratio_input_error))
- .check(
- matches(
- withText(
- R.string.ratio_error_invalid_format
- )
- )
- )
- }
- }
-
- @Test
- @DisableAccessibilityChecks // Disabled, as InputInteractionViewTestActivity is a test file and
- // will not be used by user
- fun testRatioInput_withRatioHaving4Terms_submit_invalidSizeErrorIsDisplayed() {
- ActivityScenario.launch(InputInteractionViewTestActivity::class.java).use {
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- setTextToRatioInputInteractionView(
- "1:2:3:4"
- )
- )
- closeSoftKeyboard()
- testCoroutineDispatchers.runCurrent()
- scrollToSubmitButton()
- onView(withId(R.id.submit_button)).check(matches(isDisplayed())).perform(click())
- onView(withId(R.id.ratio_input_error))
- .check(
- matches(
- withText(
- R.string.ratio_error_invalid_size
- )
- )
- )
- }
- }
-
- @Test
- @DisableAccessibilityChecks // Disabled, as InputInteractionViewTestActivity is a test file and
- // will not be used by user
- fun testRatioInput_withRatioHaving2Terms_submit_invalidSizeErrorIsDisplayed() {
- ActivityScenario.launch(InputInteractionViewTestActivity::class.java).use {
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- setTextToRatioInputInteractionView(
- "1:2"
- )
- )
- closeSoftKeyboard()
- testCoroutineDispatchers.runCurrent()
- scrollToSubmitButton()
- onView(withId(R.id.submit_button)).check(matches(isDisplayed())).perform(click())
- onView(withId(R.id.ratio_input_error))
- .check(
- matches(
- withText(
- R.string.ratio_error_invalid_size
- )
- )
- )
- }
- }
-
- @Test
- @DisableAccessibilityChecks // Disabled, as InputInteractionViewTestActivity is a test file and
- // will not be used by user
- fun testRatioInput_withRatioHaving3Terms_submit_noErrorIsDisplayed() {
- ActivityScenario.launch(InputInteractionViewTestActivity::class.java).use {
- testCoroutineDispatchers.runCurrent()
- onView(withId(R.id.test_ratio_input_interaction_view))
- .perform(
- setTextToRatioInputInteractionView(
- "1:2:3"
- )
- )
- closeSoftKeyboard()
- scrollToSubmitButton()
- onView(withId(R.id.submit_button)).check(matches(isDisplayed())).perform(click())
- onView(withId(R.id.ratio_input_error)).check(matches(withText("")))
- }
- }
-
private fun scrollToSubmitButton() {
onView(withId(R.id.submit_button)).perform(scrollTo())
testCoroutineDispatchers.runCurrent()
}
- private fun setTextToRatioInputInteractionView(
- newText: String?
- ): ViewAction? {
- return object : ViewAction {
- override fun getConstraints(): Matcher {
- return allOf(isDisplayed(), isAssignableFrom(RatioInputInteractionView::class.java))
- }
-
- override fun getDescription(): String {
- return "Update the text from the custom EditText"
- }
-
- override fun perform(uiController: UiController?, view: View) {
- (view as RatioInputInteractionView).setText(newText)
- uiController?.loopMainThreadUntilIdle()
- }
- }
- }
-
// TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them.
@Singleton
@Component(
diff --git a/app/src/sharedTest/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivityTest.kt
new file mode 100644
index 00000000000..5a8249a9088
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivityTest.kt
@@ -0,0 +1,476 @@
+package org.oppia.android.app.testing
+
+import android.app.Application
+import android.content.res.Configuration
+import android.view.View
+import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.closeSoftKeyboard
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.UiController
+import androidx.test.espresso.ViewAction
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.action.ViewActions.scrollTo
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import dagger.Component
+import org.hamcrest.CoreMatchers
+import org.hamcrest.Matcher
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.android.R
+import org.oppia.android.app.activity.ActivityComponent
+import org.oppia.android.app.activity.ActivityComponentFactory
+import org.oppia.android.app.activity.route.ActivityRouterModule
+import org.oppia.android.app.application.ApplicationComponent
+import org.oppia.android.app.application.ApplicationInjector
+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.RatioInputInteractionView
+import org.oppia.android.app.devoptions.DeveloperOptionsModule
+import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.InteractionObject
+import org.oppia.android.app.player.state.itemviewmodel.SplitScreenInteractionModule
+import org.oppia.android.app.shim.ViewBindingShimModule
+import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule
+import org.oppia.android.data.backends.gae.NetworkConfigProdModule
+import org.oppia.android.data.backends.gae.NetworkModule
+import org.oppia.android.domain.classify.InteractionsModule
+import org.oppia.android.domain.classify.rules.algebraicexpressioninput.AlgebraicExpressionInputModule
+import org.oppia.android.domain.classify.rules.continueinteraction.ContinueModule
+import org.oppia.android.domain.classify.rules.dragAndDropSortInput.DragDropSortInputModule
+import org.oppia.android.domain.classify.rules.fractioninput.FractionInputModule
+import org.oppia.android.domain.classify.rules.imageClickInput.ImageClickInputModule
+import org.oppia.android.domain.classify.rules.itemselectioninput.ItemSelectionInputModule
+import org.oppia.android.domain.classify.rules.mathequationinput.MathEquationInputModule
+import org.oppia.android.domain.classify.rules.multiplechoiceinput.MultipleChoiceInputModule
+import org.oppia.android.domain.classify.rules.numberwithunits.NumberWithUnitsRuleModule
+import org.oppia.android.domain.classify.rules.numericexpressioninput.NumericExpressionInputModule
+import org.oppia.android.domain.classify.rules.numericinput.NumericInputRuleModule
+import org.oppia.android.domain.classify.rules.ratioinput.RatioInputModule
+import org.oppia.android.domain.classify.rules.textinput.TextInputRuleModule
+import org.oppia.android.domain.exploration.ExplorationProgressModule
+import org.oppia.android.domain.exploration.ExplorationStorageModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionConfigModule
+import org.oppia.android.domain.hintsandsolution.HintsAndSolutionProdModule
+import org.oppia.android.domain.onboarding.ExpirationMetaDataRetrieverModule
+import org.oppia.android.domain.oppialogger.LogStorageModule
+import org.oppia.android.domain.oppialogger.LoggingIdentifierModule
+import org.oppia.android.domain.oppialogger.analytics.ApplicationLifecycleModule
+import org.oppia.android.domain.oppialogger.analytics.CpuPerformanceSnapshotterModule
+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.PrimeTopicAssetsControllerModule
+import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
+import org.oppia.android.testing.DisableAccessibilityChecks
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.espresso.EditTextInputAction
+import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
+import org.oppia.android.testing.robolectric.RobolectricModule
+import org.oppia.android.testing.threading.TestCoroutineDispatchers
+import org.oppia.android.testing.threading.TestDispatcherModule
+import org.oppia.android.testing.time.FakeOppiaClockModule
+import org.oppia.android.util.accessibility.AccessibilityTestModule
+import org.oppia.android.util.caching.AssetModule
+import org.oppia.android.util.caching.testing.CachingTestModule
+import org.oppia.android.util.gcsresource.GcsResourceModule
+import org.oppia.android.util.locale.LocaleProdModule
+import org.oppia.android.util.logging.EventLoggingConfigurationModule
+import org.oppia.android.util.logging.LoggerModule
+import org.oppia.android.util.logging.SyncStatusModule
+import org.oppia.android.util.logging.firebase.FirebaseLogUploaderModule
+import org.oppia.android.util.networking.NetworkConnectionDebugUtilModule
+import org.oppia.android.util.networking.NetworkConnectionUtilDebugModule
+import org.oppia.android.util.parser.html.HtmlParserEntityTypeModule
+import org.oppia.android.util.parser.image.GlideImageLoaderModule
+import org.oppia.android.util.parser.image.ImageParsingModule
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.LooperMode
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/** Tests for [RatioInputInteractionViewTestActivity]. */
+@RunWith(AndroidJUnit4::class)
+@LooperMode(LooperMode.Mode.PAUSED)
+@Config(
+ application = RatioInputInteractionViewTestActivityTest.TestApplication::class,
+ qualifiers = "port-xxhdpi"
+)
+class RatioInputInteractionViewTestActivityTest {
+ @get:Rule
+ val initializeDefaultLocaleRule = InitializeDefaultLocaleRule()
+
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
+
+ @Inject
+ lateinit var editTextInputAction: EditTextInputAction
+
+ @Before
+ fun setUp() {
+ setUpTestApplicationComponent()
+ testCoroutineDispatchers.registerIdlingResource()
+ }
+
+ @After
+ fun tearDown() {
+ testCoroutineDispatchers.unregisterIdlingResource()
+ }
+
+ private fun setUpTestApplicationComponent() {
+ ApplicationProvider.getApplicationContext().inject(this)
+ }
+
+ @Test
+ fun testRatioInput_withNoInput_hasCorrectPendingAnswerType() {
+ val activityScenario = ActivityScenario.launch(
+ RatioInputInteractionViewTestActivity::class.java
+ )
+ activityScenario.onActivity { activity ->
+ val pendingAnswer = activity.ratioExpressionInputInteractionViewModel.getPendingAnswer()
+ assertThat(pendingAnswer.answer).isInstanceOf(InteractionObject::class.java)
+ assertThat(pendingAnswer.answer.ratioExpression.ratioComponentCount).isEqualTo(0)
+ }
+ }
+
+ @Test
+ fun testRatioInput_withRatioOfNumber_hasCorrectPendingAnswer() {
+ val activityScenario = ActivityScenario.launch(
+ RatioInputInteractionViewTestActivity::class.java
+ )
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ setTextToRatioInputInteractionView(
+ "1:2:3"
+ )
+ )
+ activityScenario.onActivity { activity ->
+ val pendingAnswer = activity.ratioExpressionInputInteractionViewModel.getPendingAnswer()
+ assertThat(pendingAnswer.answer).isInstanceOf(InteractionObject::class.java)
+ assertThat(pendingAnswer.answer.objectTypeCase).isEqualTo(
+ InteractionObject.ObjectTypeCase.RATIO_EXPRESSION
+ )
+ assertThat(pendingAnswer.answer.ratioExpression.ratioComponentList)
+ .isEqualTo(listOf(1, 2, 3))
+ }
+ }
+
+ @Test
+ @Ignore("Landscape not properly supported") // TODO(#56): Reenable once landscape is supported.
+ fun testRatioInput_withRatio_configChange_hasCorrectPendingAnswer() {
+ val activityScenario = ActivityScenario.launch(
+ RatioInputInteractionViewTestActivity::class.java
+ )
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ editTextInputAction.appendText(
+ "1:2"
+ )
+ )
+ activityScenario.onActivity { activity ->
+ activity.requestedOrientation = Configuration.ORIENTATION_LANDSCAPE
+ }
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .check(matches(isDisplayed()))
+ .check(matches(withText("1:2")))
+ }
+
+ @Test
+ fun testRatioInput_withTwoColonsTogether_colonsTogetherFormatErrorIsDisplayed() {
+ ActivityScenario.launch(RatioInputInteractionViewTestActivity::class.java).use {
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ setTextToRatioInputInteractionView(
+ "1::2"
+ )
+ )
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.ratio_input_error))
+ .check(
+ matches(
+ withText(
+ R.string.ratio_error_invalid_colons
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testRatioInput_withNegativeRatioOfNumber_numberFormatErrorIsDisplayed() {
+ ActivityScenario.launch(RatioInputInteractionViewTestActivity::class.java).use {
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ setTextToRatioInputInteractionView(
+ "-1:2:3:4"
+ )
+ )
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.ratio_input_error))
+ .check(
+ matches(
+ withText(
+ R.string.ratio_error_invalid_chars
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ @DisableAccessibilityChecks // Disabled, as RatioInputInteractionViewTestActivity is a test file and
+ // will not be used by user
+ fun testRatioInput_withBlankInput_submit_numberWithZerosErrorIsDisplayed() {
+ ActivityScenario.launch(RatioInputInteractionViewTestActivity::class.java).use {
+ testCoroutineDispatchers.runCurrent()
+ scrollToSubmitButton()
+ onView(withId(R.id.submit_button))
+ .check(matches(isDisplayed())).perform(click())
+ onView(withId(R.id.ratio_input_error))
+ .check(
+ matches(
+ withText(
+ R.string.ratio_error_empty_input
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ @DisableAccessibilityChecks // Disabled, as RatioInputInteractionViewTestActivity is a test file and
+ // will not be used by user
+ fun testRatioInput_withZeroRatio_submit_numberWithZerosErrorIsDisplayed() {
+ ActivityScenario.launch(RatioInputInteractionViewTestActivity::class.java).use {
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ setTextToRatioInputInteractionView(
+ "1:0:4"
+ )
+ )
+ testCoroutineDispatchers.runCurrent()
+ scrollToSubmitButton()
+ onView(withId(R.id.submit_button))
+ .check(matches(isDisplayed())).perform(click())
+ onView(withId(R.id.ratio_input_error))
+ .check(
+ matches(
+ withText(
+ R.string.ratio_error_includes_zero
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ @DisableAccessibilityChecks // Disabled, as RatioInputInteractionViewTestActivity is a test file and
+ // will not be used by user
+ fun testRatioInput_withInvalidRatio_submit_numberFormatErrorIsDisplayed() {
+ ActivityScenario.launch(RatioInputInteractionViewTestActivity::class.java).use {
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ setTextToRatioInputInteractionView(
+ "1: 1 2 :4"
+ )
+ )
+ closeSoftKeyboard()
+ testCoroutineDispatchers.runCurrent()
+ scrollToSubmitButton()
+ onView(withId(R.id.submit_button))
+ .check(matches(isDisplayed())).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.ratio_input_error))
+ .check(
+ matches(
+ withText(
+ R.string.ratio_error_invalid_format
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ @DisableAccessibilityChecks // Disabled, as RatioInputInteractionViewTestActivity is a test file and
+ // will not be used by user
+ fun testRatioInput_withRatioHaving4Terms_submit_invalidSizeErrorIsDisplayed() {
+ ActivityScenario.launch(RatioInputInteractionViewTestActivity::class.java).use {
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ setTextToRatioInputInteractionView(
+ "1:2:3:4"
+ )
+ )
+ closeSoftKeyboard()
+ testCoroutineDispatchers.runCurrent()
+ scrollToSubmitButton()
+ onView(withId(R.id.submit_button))
+ .check(matches(isDisplayed())).perform(click())
+ onView(withId(R.id.ratio_input_error))
+ .check(
+ matches(
+ withText(
+ R.string.ratio_error_invalid_size
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ @DisableAccessibilityChecks // Disabled, as RatioInputInteractionViewTestActivity is a test file and
+ // will not be used by user
+ fun testRatioInput_withRatioHaving2Terms_submit_invalidSizeErrorIsDisplayed() {
+ ActivityScenario.launch(RatioInputInteractionViewTestActivity::class.java).use {
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ setTextToRatioInputInteractionView(
+ "1:2"
+ )
+ )
+ closeSoftKeyboard()
+ testCoroutineDispatchers.runCurrent()
+ scrollToSubmitButton()
+ onView(withId(R.id.submit_button))
+ .check(matches(isDisplayed())).perform(click())
+ onView(withId(R.id.ratio_input_error))
+ .check(
+ matches(
+ withText(
+ R.string.ratio_error_invalid_size
+ )
+ )
+ )
+ }
+ }
+
+ @Test
+ @DisableAccessibilityChecks // Disabled, as RatioInputInteractionViewTestActivity is a test file and
+ // will not be used by user
+ fun testRatioInput_withRatioHaving3Terms_submit_noErrorIsDisplayed() {
+ ActivityScenario.launch(RatioInputInteractionViewTestActivity::class.java).use {
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.test_ratio_input_interaction_view))
+ .perform(
+ setTextToRatioInputInteractionView(
+ "1:2:3"
+ )
+ )
+ closeSoftKeyboard()
+ scrollToSubmitButton()
+ onView(withId(R.id.submit_button))
+ .check(matches(isDisplayed())).perform(click())
+ onView(withId(R.id.ratio_input_error))
+ .check(matches(withText("")))
+ }
+ }
+
+ private fun scrollToSubmitButton() {
+ onView(withId(R.id.submit_button)).perform(scrollTo())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun setTextToRatioInputInteractionView(
+ newText: String?
+ ): ViewAction? {
+ return object : ViewAction {
+ override fun getConstraints(): Matcher {
+ return CoreMatchers.allOf(
+ isDisplayed(),
+ isAssignableFrom(RatioInputInteractionView::class.java)
+ )
+ }
+
+ override fun getDescription(): String {
+ return "Update the text from the custom EditText"
+ }
+
+ override fun perform(uiController: UiController?, view: View) {
+ (view as RatioInputInteractionView).setText(newText)
+ uiController?.loopMainThreadUntilIdle()
+ }
+ }
+ }
+
+ // TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them.
+ @Singleton
+ @Component(
+ modules = [
+ RobolectricModule::class,
+ PlatformParameterModule::class, PlatformParameterSingletonModule::class,
+ TestDispatcherModule::class, ApplicationModule::class,
+ LoggerModule::class, ContinueModule::class, FractionInputModule::class,
+ ItemSelectionInputModule::class, MultipleChoiceInputModule::class,
+ NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, TextInputRuleModule::class,
+ DragDropSortInputModule::class, ImageClickInputModule::class, InteractionsModule::class,
+ GcsResourceModule::class, GlideImageLoaderModule::class, ImageParsingModule::class,
+ HtmlParserEntityTypeModule::class, QuestionModule::class, TestLogReportingModule::class,
+ AccessibilityTestModule::class, LogStorageModule::class, CachingTestModule::class,
+ PrimeTopicAssetsControllerModule::class, ExpirationMetaDataRetrieverModule::class,
+ ViewBindingShimModule::class, RatioInputModule::class, WorkManagerConfigurationModule::class,
+ ApplicationStartupListenerModule::class, LogReportWorkerModule::class,
+ HintsAndSolutionConfigModule::class, HintsAndSolutionProdModule::class,
+ FirebaseLogUploaderModule::class, FakeOppiaClockModule::class,
+ DeveloperOptionsStarterModule::class, DeveloperOptionsModule::class,
+ ExplorationStorageModule::class, NetworkModule::class, NetworkConfigProdModule::class,
+ NetworkConnectionUtilDebugModule::class, NetworkConnectionDebugUtilModule::class,
+ AssetModule::class, LocaleProdModule::class, ActivityRecreatorTestModule::class,
+ NumericExpressionInputModule::class, AlgebraicExpressionInputModule::class,
+ MathEquationInputModule::class, SplitScreenInteractionModule::class,
+ LoggingIdentifierModule::class, ApplicationLifecycleModule::class,
+ SyncStatusModule::class, MetricLogSchedulerModule::class, TestingBuildFlavorModule::class,
+ EventLoggingConfigurationModule::class, ActivityRouterModule::class,
+ CpuPerformanceSnapshotterModule::class, ExplorationProgressModule::class
+ ]
+ )
+ interface TestApplicationComponent : ApplicationComponent {
+ @Component.Builder
+ interface Builder : ApplicationComponent.Builder
+
+ fun inject(ratioInputInteractionViewTestActivityTest: RatioInputInteractionViewTestActivityTest)
+ }
+
+ class TestApplication : Application(), ActivityComponentFactory, ApplicationInjectorProvider {
+ private val component: TestApplicationComponent by lazy {
+ DaggerRatioInputInteractionViewTestActivityTest_TestApplicationComponent.builder()
+ .setApplication(this)
+ .build() as TestApplicationComponent
+ }
+
+ fun inject(inputInteractionViewTestActivityTest: RatioInputInteractionViewTestActivityTest) {
+ component.inject(inputInteractionViewTestActivityTest)
+ }
+
+ override fun createActivityComponent(activity: AppCompatActivity): ActivityComponent {
+ return component.getActivityComponentBuilderProvider().get().setActivity(activity).build()
+ }
+
+ override fun getApplicationInjector(): ApplicationInjector = component
+ }
+}
diff --git a/scripts/assets/accessibility_label_exemptions.textproto b/scripts/assets/accessibility_label_exemptions.textproto
index dd1db64c796..4f2d9616133 100644
--- a/scripts/assets/accessibility_label_exemptions.textproto
+++ b/scripts/assets/accessibility_label_exemptions.textproto
@@ -22,6 +22,7 @@ exempted_activity: "app/src/main/java/org/oppia/android/app/testing/HtmlParserTe
exempted_activity: "app/src/main/java/org/oppia/android/app/testing/ImageRegionSelectionTestActivity"
exempted_activity: "app/src/main/java/org/oppia/android/app/testing/ImageViewBindingAdaptersTestActivity"
exempted_activity: "app/src/main/java/org/oppia/android/app/testing/InputInteractionViewTestActivity"
+exempted_activity: "app/src/main/java/org/oppia/android/app/testing/RatioInputInteractionViewTestActivity"
exempted_activity: "app/src/main/java/org/oppia/android/app/testing/LessonThumbnailImageViewTestActivity"
exempted_activity: "app/src/main/java/org/oppia/android/app/testing/ListItemLeadingMarginSpanTestActivity"
exempted_activity: "app/src/main/java/org/oppia/android/app/testing/MarginBindingAdaptersTestActivity"