diff --git a/.gitignore b/.gitignore
index f9250e37d80..6d3b741a2f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,4 @@ config/oppia-dev-workflow-remote-cache-credentials.json
bazel-*
.bazelproject
.aswb
+*.pb
diff --git a/app/BUILD.bazel b/app/BUILD.bazel
index da637339c1a..fba131c801b 100644
--- a/app/BUILD.bazel
+++ b/app/BUILD.bazel
@@ -17,7 +17,10 @@ load("@tools_android//tools/googleservices:defs.bzl", "google_services_xml")
load("//app:app_test.bzl", "app_test")
load("//app:test_with_resources.bzl", "test_with_resources")
-package(default_visibility = ["//utility:__subpackages__"])
+package(default_visibility = [
+ "//domain:__subpackages__",
+ "//utility:__subpackages__",
+])
exports_files(["src/main/AndroidManifest.xml"])
@@ -792,6 +795,7 @@ TEST_DEPS = [
"//app/src/main/java/org/oppia/android/app/translation/testing:test_module",
"//domain",
"//testing",
+ "//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/espresso:generic_view_matchers",
"//testing/src/main/java/org/oppia/android/testing/espresso:image_view_matcher",
diff --git a/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragment.kt b/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragment.kt
index 6bf2f3492a5..af9f96121d1 100644
--- a/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragment.kt
@@ -10,6 +10,7 @@ import org.oppia.android.app.fragment.FragmentComponentImpl
import org.oppia.android.app.fragment.InjectableDialogFragment
import org.oppia.android.app.model.HelpIndex
import org.oppia.android.app.model.State
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.util.extensions.getProto
import org.oppia.android.util.extensions.getStringFromBundle
import org.oppia.android.util.extensions.putProto
@@ -44,25 +45,32 @@ class HintsAndSolutionDialogFragment :
internal const val ID_ARGUMENT_KEY = "HintsAndSolutionDialogFragment.id"
internal const val STATE_KEY = "HintsAndSolutionDialogFragment.state"
internal const val HELP_INDEX_KEY = "HintsAndSolutionDialogFragment.help_index"
+ internal const val WRITTEN_TRANSLATION_CONTEXT_KEY =
+ "HintsAndSolutionDialogFragment.written_translation_context"
/**
* Creates a new instance of a DialogFragment to display hints and solution
*
- * @param id Used in ExplorationController/QuestionAssessmentProgressController to get current state data.
+ * @param id Used in ExplorationController/QuestionAssessmentProgressController to get current
+ * state data.
* @param state the [State] being viewed by the learner
* @param helpIndex the [HelpIndex] corresponding to the current hints/solution configuration
+ * @param writtenTranslationContext the [WrittenTranslationContext] needed to translate the
+ * hints/solution
* @return [HintsAndSolutionDialogFragment]: DialogFragment
*/
fun newInstance(
id: String,
state: State,
- helpIndex: HelpIndex
+ helpIndex: HelpIndex,
+ writtenTranslationContext: WrittenTranslationContext
): HintsAndSolutionDialogFragment {
return HintsAndSolutionDialogFragment().apply {
arguments = Bundle().apply {
putString(ID_ARGUMENT_KEY, id)
putProto(STATE_KEY, state)
putProto(HELP_INDEX_KEY, helpIndex)
+ putProto(WRITTEN_TRANSLATION_CONTEXT_KEY, writtenTranslationContext)
}
}
}
@@ -107,12 +115,15 @@ class HintsAndSolutionDialogFragment :
val state = args.getProto(STATE_KEY, State.getDefaultInstance())
val helpIndex = args.getProto(HELP_INDEX_KEY, HelpIndex.getDefaultInstance())
+ val writtenTranslationContext =
+ args.getProto(WRITTEN_TRANSLATION_CONTEXT_KEY, WrittenTranslationContext.getDefaultInstance())
return hintsAndSolutionDialogFragmentPresenter.handleCreateView(
inflater,
container,
state,
helpIndex,
+ writtenTranslationContext,
id,
currentExpandedHintListIndex,
this as ExpandedHintListIndexListener,
diff --git a/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragmentPresenter.kt
index a7038603c33..50a77998d9e 100644
--- a/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsAndSolutionDialogFragmentPresenter.kt
@@ -12,6 +12,7 @@ import org.oppia.android.app.model.HelpIndex.IndexTypeCase.LATEST_REVEALED_HINT_
import org.oppia.android.app.model.HelpIndex.IndexTypeCase.NEXT_AVAILABLE_HINT_INDEX
import org.oppia.android.app.model.HelpIndex.IndexTypeCase.SHOW_SOLUTION
import org.oppia.android.app.model.State
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.recyclerview.BindableAdapter
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.app.viewmodel.ViewModelProvider
@@ -47,6 +48,7 @@ class HintsAndSolutionDialogFragmentPresenter @Inject constructor(
private lateinit var binding: HintsAndSolutionFragmentBinding
private lateinit var state: State
private lateinit var helpIndex: HelpIndex
+ private lateinit var writtenTranslationContext: WrittenTranslationContext
private lateinit var itemList: List
private lateinit var bindingAdapter: BindableAdapter
@@ -63,6 +65,7 @@ class HintsAndSolutionDialogFragmentPresenter @Inject constructor(
container: ViewGroup?,
state: State,
helpIndex: HelpIndex,
+ writtenTranslationContext: WrittenTranslationContext,
id: String?,
currentExpandedHintListIndex: Int?,
expandedHintListIndexListener: ExpandedHintListIndexListener,
@@ -93,6 +96,7 @@ class HintsAndSolutionDialogFragmentPresenter @Inject constructor(
this.state = state
this.helpIndex = helpIndex
+ this.writtenTranslationContext = writtenTranslationContext
// The newAvailableHintIndex received here is coming from state player but in this
// implementation hints/solutions are shown on every even index and on every odd index we show a
// divider. The relative index therefore needs to be doubled to account for the divider.
@@ -137,7 +141,9 @@ class HintsAndSolutionDialogFragmentPresenter @Inject constructor(
private fun loadHintsAndSolution(state: State) {
// Check if hints are available for this state.
if (state.interaction.hintList.isNotEmpty()) {
- viewModel.initialize(helpIndex, state.interaction.hintList, state.interaction.solution)
+ viewModel.initialize(
+ helpIndex, state.interaction.hintList, state.interaction.solution, writtenTranslationContext
+ )
itemList = viewModel.processHintList()
diff --git a/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsViewModel.kt b/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsViewModel.kt
index 36b5af0b090..20de8ea0b51 100644
--- a/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/hintsandsolution/HintsViewModel.kt
@@ -7,9 +7,11 @@ import org.oppia.android.app.fragment.FragmentScope
import org.oppia.android.app.model.HelpIndex
import org.oppia.android.app.model.Hint
import org.oppia.android.app.model.Solution
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.domain.hintsandsolution.isHintRevealed
import org.oppia.android.domain.hintsandsolution.isSolutionRevealed
+import org.oppia.android.domain.translation.TranslationController
import javax.inject.Inject
/**
@@ -24,7 +26,8 @@ private const val DEFAULT_HINT_AND_SOLUTION_SUMMARY = ""
/** [ViewModel] for Hints in [HintsAndSolutionDialogFragment]. */
@FragmentScope
class HintsViewModel @Inject constructor(
- private val resourceHandler: AppLanguageResourceHandler
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val translationController: TranslationController
) : HintsAndSolutionItemViewModel() {
val newAvailableHintIndex = ObservableField(-1)
@@ -39,13 +42,20 @@ class HintsViewModel @Inject constructor(
private lateinit var hintList: List
private lateinit var solution: Solution
private lateinit var helpIndex: HelpIndex
+ private lateinit var writtenTranslationContext: WrittenTranslationContext
val itemList: MutableList = ArrayList()
/** Initializes the view model to display hints and a solution. */
- fun initialize(helpIndex: HelpIndex, hintList: List, solution: Solution) {
+ fun initialize(
+ helpIndex: HelpIndex,
+ hintList: List,
+ solution: Solution,
+ writtenTranslationContext: WrittenTranslationContext
+ ) {
this.helpIndex = helpIndex
this.hintList = hintList
this.solution = solution
+ this.writtenTranslationContext = writtenTranslationContext
}
fun processHintList(): List {
@@ -93,9 +103,11 @@ class HintsViewModel @Inject constructor(
}
private fun addHintToList(hintIndex: Int, hint: Hint) {
- val hintsViewModel = HintsViewModel(resourceHandler)
+ val hintsViewModel = HintsViewModel(resourceHandler, translationController)
hintsViewModel.title.set(hint.hintContent.contentId)
- hintsViewModel.hintsAndSolutionSummary.set(hint.hintContent.html)
+ val hintContentHtml =
+ translationController.extractString(hint.hintContent, writtenTranslationContext)
+ hintsViewModel.hintsAndSolutionSummary.set(hintContentHtml)
hintsViewModel.isHintRevealed.set(helpIndex.isHintRevealed(hintIndex, hintList))
itemList.add(hintsViewModel)
addDividerItem()
@@ -109,7 +121,9 @@ class HintsViewModel @Inject constructor(
solutionViewModel.denominator.set(solution.correctAnswer.denominator)
solutionViewModel.wholeNumber.set(solution.correctAnswer.wholeNumber)
solutionViewModel.isNegative.set(solution.correctAnswer.isNegative)
- solutionViewModel.solutionSummary.set(solution.explanation.html)
+ val explanationHtml =
+ translationController.extractString(solution.explanation, writtenTranslationContext)
+ solutionViewModel.solutionSummary.set(explanationHtml)
solutionViewModel.isSolutionRevealed.set(helpIndex.isSolutionRevealed())
itemList.add(solutionViewModel)
addDividerItem()
diff --git a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragmentPresenter.kt
index 0968ec1f88b..996d08cb7ce 100755
--- a/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/home/recentlyplayed/RecentlyPlayedFragmentPresenter.kt
@@ -241,7 +241,9 @@ class RecentlyPlayedFragmentPresenter @Inject constructor(
if (promotedStory.chapterPlayState == ChapterPlayState.IN_PROGRESS_SAVED) {
val explorationCheckpointLiveData =
explorationCheckpointController.retrieveExplorationCheckpoint(
- ProfileId.getDefaultInstance(),
+ ProfileId.newBuilder().apply {
+ internalId = internalProfileId
+ }.build(),
promotedStory.explorationId
).toLiveData()
diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt
index ccbde8f7948..72d9a729f69 100755
--- a/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/player/exploration/ExplorationActivity.kt
@@ -15,6 +15,7 @@ import org.oppia.android.app.hintsandsolution.RevealSolutionInterface
import org.oppia.android.app.model.HelpIndex
import org.oppia.android.app.model.ReadingTextSize
import org.oppia.android.app.model.State
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.audio.AudioButtonListener
import org.oppia.android.app.player.state.listener.RouteToHintsAndSolutionListener
import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener
@@ -45,6 +46,7 @@ class ExplorationActivity :
private lateinit var storyId: String
private lateinit var explorationId: String
private lateinit var state: State
+ private lateinit var writtenTranslationContext: WrittenTranslationContext
private var backflowScreen: Int? = null
private var isCheckpointingEnabled: Boolean = false
@@ -165,7 +167,8 @@ class ExplorationActivity :
val hintsAndSolutionDialogFragment = HintsAndSolutionDialogFragment.newInstance(
explorationId,
state,
- helpIndex
+ helpIndex,
+ writtenTranslationContext
)
hintsAndSolutionDialogFragment.showNow(supportFragmentManager, TAG_HINTS_AND_SOLUTION_DIALOG)
}
@@ -179,8 +182,12 @@ class ExplorationActivity :
explorationActivityPresenter.loadExplorationFragment(readingTextSize)
}
- override fun onExplorationStateLoaded(state: State) {
+ override fun onExplorationStateLoaded(
+ state: State,
+ writtenTranslationContext: WrittenTranslationContext
+ ) {
this.state = state
+ this.writtenTranslationContext = writtenTranslationContext
}
override fun dismissConceptCard() = explorationActivityPresenter.dismissConceptCard()
diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/HintsAndSolutionExplorationManagerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/player/exploration/HintsAndSolutionExplorationManagerFragmentPresenter.kt
index 7b71557a0bd..a8460e014ff 100644
--- a/app/src/main/java/org/oppia/android/app/player/exploration/HintsAndSolutionExplorationManagerFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/player/exploration/HintsAndSolutionExplorationManagerFragmentPresenter.kt
@@ -57,7 +57,7 @@ class HintsAndSolutionExplorationManagerFragmentPresenter @Inject constructor(
// Check if hints are available for this state.
if (ephemeralState.state.interaction.hintList.size != 0) {
(activity as HintsAndSolutionExplorationManagerListener).onExplorationStateLoaded(
- ephemeralState.state
+ ephemeralState.state, ephemeralState.writtenTranslationContext
)
}
}
diff --git a/app/src/main/java/org/oppia/android/app/player/exploration/HintsAndSolutionExplorationManagerListener.kt b/app/src/main/java/org/oppia/android/app/player/exploration/HintsAndSolutionExplorationManagerListener.kt
index 9b671eb020e..c728304b65e 100644
--- a/app/src/main/java/org/oppia/android/app/player/exploration/HintsAndSolutionExplorationManagerListener.kt
+++ b/app/src/main/java/org/oppia/android/app/player/exploration/HintsAndSolutionExplorationManagerListener.kt
@@ -1,8 +1,9 @@
package org.oppia.android.app.player.exploration
import org.oppia.android.app.model.State
+import org.oppia.android.app.model.WrittenTranslationContext
/** Listener for fetching current exploration state data. */
interface HintsAndSolutionExplorationManagerListener {
- fun onExplorationStateLoaded(state: State)
+ fun onExplorationStateLoaded(state: State, writtenTranslationContext: WrittenTranslationContext)
}
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 4bd48436b97..0a0f06df55c 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,6 +7,7 @@ import androidx.databinding.BindingAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
+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
import org.oppia.android.app.recyclerview.BindableAdapter
@@ -45,6 +46,7 @@ class SelectionInteractionView @JvmOverloads constructor(
lateinit var bindingInterface: ViewBindingShim
private lateinit var entityId: String
+ private lateinit var writtenTranslationContext: WrittenTranslationContext
override fun onAttachedToWindow() {
super.onAttachedToWindow()
@@ -69,6 +71,15 @@ class SelectionInteractionView @JvmOverloads constructor(
this.entityId = entityId
}
+ /**
+ * Sets the [WrittenTranslationContext] used to translate strings in this view.
+ *
+ * This must be called during view initialization.
+ */
+ fun setWrittenTranslationContext(writtenTranslationContext: WrittenTranslationContext) {
+ this.writtenTranslationContext = writtenTranslationContext
+ }
+
private fun createAdapter(): BindableAdapter {
return when (selectionItemInputType) {
SelectionItemInputType.CHECKBOXES ->
@@ -89,7 +100,8 @@ class SelectionInteractionView @JvmOverloads constructor(
htmlParserFactory,
resourceBucketName,
entityType,
- entityId
+ entityId,
+ writtenTranslationContext
)
}
)
@@ -112,7 +124,8 @@ class SelectionInteractionView @JvmOverloads constructor(
htmlParserFactory,
resourceBucketName,
entityType,
- entityId
+ entityId,
+ writtenTranslationContext
)
}
)
@@ -127,3 +140,10 @@ fun setEntityId(
selectionInteractionView: SelectionInteractionView,
entityId: String
) = selectionInteractionView.setEntityId(entityId)
+
+/** Sets the translation context for a specific [SelectionInteractionView] via data-binding. */
+@BindingAdapter("writtenTranslationContext")
+fun setWrittenTranslationContext(
+ selectionInteractionView: SelectionInteractionView,
+ writtenTranslationContext: WrittenTranslationContext
+) = selectionInteractionView.setWrittenTranslationContext(writtenTranslationContext)
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 30a57cce8c5..61bd4999f07 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
@@ -112,7 +112,7 @@ class StateFragmentPresenter @Inject constructor(
/* attachToRoot= */ false
)
recyclerViewAssembler = createRecyclerViewAssembler(
- assemblerBuilderFactory.create(resourceBucketName, entityType),
+ assemblerBuilderFactory.create(resourceBucketName, entityType, profileId),
binding.congratulationsTextView,
binding.congratulationsTextConfettiView,
binding.fullScreenConfettiView
@@ -275,7 +275,7 @@ class StateFragmentPresenter @Inject constructor(
private fun subscribeToCurrentState() {
ephemeralStateLiveData.observe(
fragment,
- Observer { result ->
+ { result ->
processEphemeralStateResult(result)
}
)
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 58831b7aa0e..99e21d420d7 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
@@ -23,9 +23,11 @@ import org.oppia.android.app.model.EphemeralState
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.StringList
import org.oppia.android.app.model.SubtitledHtml
import org.oppia.android.app.model.UserAnswer
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.audio.AudioUiManager
import org.oppia.android.app.player.state.StatePlayerRecyclerViewAssembler.Builder.Factory
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver
@@ -84,6 +86,7 @@ import org.oppia.android.databinding.SubmittedAnswerItemBinding
import org.oppia.android.databinding.SubmittedAnswerListItemBinding
import org.oppia.android.databinding.SubmittedHtmlAnswerItemBinding
import org.oppia.android.databinding.TextInputInteractionItemBinding
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.util.parser.html.HtmlParser
import org.oppia.android.util.threading.BackgroundDispatcher
import javax.inject.Inject
@@ -120,6 +123,7 @@ class StatePlayerRecyclerViewAssembler private constructor(
val rhsAdapter: BindableAdapter,
private val playerFeatureSet: PlayerFeatureSet,
private val fragment: Fragment,
+ private val profileId: ProfileId,
private val context: Context,
private val congratulationsTextView: TextView?,
private val congratulationsTextConfettiView: KonfettiView?,
@@ -135,7 +139,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
String, @JvmSuppressWildcards InteractionViewModelFactory>,
backgroundCoroutineDispatcher: CoroutineDispatcher,
private val hasConversationView: Boolean,
- private val resourceHandler: AppLanguageResourceHandler
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val translationController: TranslationController
) : HtmlParser.CustomOppiaTagActionListener {
/**
* A list of view models corresponding to past view models that are hidden by default. These are
@@ -176,7 +181,7 @@ class StatePlayerRecyclerViewAssembler private constructor(
override fun onConceptCardLinkClicked(view: View, skillId: String) {
ConceptCardFragment
- .newInstance(skillId)
+ .newInstance(skillId, profileId)
.showNow(fragment.childFragmentManager, CONCEPT_CARD_DIALOG_FRAGMENT_TAG)
}
@@ -213,7 +218,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
extraInteractionPendingItemList,
ephemeralState.pendingState.wrongAnswerList,
/* isCorrectAnswer= */ false,
- gcsEntityId
+ gcsEntityId,
+ ephemeralState.writtenTranslationContext
)
if (playerFeatureSet.interactionSupport) {
val interactionItemList =
@@ -222,7 +228,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
interactionItemList,
interaction,
hasPreviousState,
- gcsEntityId
+ gcsEntityId,
+ ephemeralState.writtenTranslationContext
)
}
} else if (ephemeralState.stateTypeCase == StateTypeCase.COMPLETED_STATE) {
@@ -242,7 +249,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
extraInteractionPendingItemList,
ephemeralState.completedState.answerList,
/* isCorrectAnswer= */ true,
- gcsEntityId
+ gcsEntityId,
+ ephemeralState.writtenTranslationContext
)
}
@@ -295,7 +303,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
pendingItemList: MutableList,
interaction: Interaction,
hasPreviousButton: Boolean,
- gcsEntityId: String
+ gcsEntityId: String,
+ writtenTranslationContext: WrittenTranslationContext
) {
val interactionViewModelFactory = interactionViewModelFactoryMap.getValue(interaction.id)
pendingItemList += interactionViewModelFactory(
@@ -305,7 +314,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
fragment as InteractionAnswerReceiver,
fragment as InteractionAnswerErrorOrAvailabilityCheckReceiver,
hasPreviousButton,
- isSplitView.get()!!
+ isSplitView.get()!!,
+ writtenTranslationContext
)
}
@@ -314,9 +324,12 @@ class StatePlayerRecyclerViewAssembler private constructor(
ephemeralState: EphemeralState,
gcsEntityId: String
) {
- val contentSubtitledHtml: SubtitledHtml = ephemeralState.state.content
+ val contentSubtitledHtml =
+ translationController.extractString(
+ ephemeralState.state.content, ephemeralState.writtenTranslationContext
+ )
pendingItemList += ContentViewModel(
- contentSubtitledHtml.html,
+ contentSubtitledHtml,
gcsEntityId,
hasConversationView,
isSplitView.get()!!,
@@ -329,7 +342,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
rightPendingItemList: MutableList,
answersAndResponses: List,
isCorrectAnswer: Boolean,
- gcsEntityId: String
+ gcsEntityId: String,
+ writtenTranslationContext: WrittenTranslationContext
) {
if (answersAndResponses.size > 1) {
if (playerFeatureSet.wrongAnswerCollapsing) {
@@ -364,7 +378,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
if (playerFeatureSet.feedbackSupport) {
createFeedbackItem(
answerAndResponse.feedback,
- gcsEntityId
+ gcsEntityId,
+ writtenTranslationContext
)?.let { viewModel ->
if (showPreviousAnswers) {
pendingItemList += viewModel
@@ -391,7 +406,7 @@ class StatePlayerRecyclerViewAssembler private constructor(
}
}
if (playerFeatureSet.feedbackSupport) {
- createFeedbackItem(answerAndResponse.feedback, gcsEntityId)?.let(
+ createFeedbackItem(answerAndResponse.feedback, gcsEntityId, writtenTranslationContext)?.let(
pendingItemList::add
)
}
@@ -539,12 +554,14 @@ class StatePlayerRecyclerViewAssembler private constructor(
private fun createFeedbackItem(
feedback: SubtitledHtml,
- gcsEntityId: String
+ gcsEntityId: String,
+ writtenTranslationContext: WrittenTranslationContext
): FeedbackViewModel? {
// Only show feedback if there's some to show.
- if (feedback.html.isNotEmpty()) {
+ val feedbackHtml = translationController.extractString(feedback, writtenTranslationContext)
+ if (feedbackHtml.isNotEmpty()) {
return FeedbackViewModel(
- feedback.html,
+ feedbackHtml,
gcsEntityId,
hasConversationView,
isSplitView.get()!!,
@@ -849,10 +866,12 @@ class StatePlayerRecyclerViewAssembler private constructor(
private val resourceBucketName: String,
private val entityType: String,
private val fragment: Fragment,
+ private val profileId: ProfileId,
private val context: Context,
private val interactionViewModelFactoryMap: Map,
private val backgroundCoroutineDispatcher: CoroutineDispatcher,
- private val resourceHandler: AppLanguageResourceHandler
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val translationController: TranslationController
) {
private val adapterBuilder = BindableAdapter.MultiTypeBuilder.newBuilder(
StateItemViewModel::viewType
@@ -1300,6 +1319,7 @@ class StatePlayerRecyclerViewAssembler private constructor(
/* rhsAdapter= */ adapterBuilder.build(),
playerFeatureSet,
fragment,
+ profileId,
context,
congratulationsTextView,
congratulationsTextConfettiView,
@@ -1314,7 +1334,8 @@ class StatePlayerRecyclerViewAssembler private constructor(
interactionViewModelFactoryMap,
backgroundCoroutineDispatcher,
hasConversationView,
- resourceHandler
+ resourceHandler,
+ translationController
)
if (playerFeatureSet.conceptCardSupport) {
customTagListener.proxyListener = assembler
@@ -1330,22 +1351,25 @@ class StatePlayerRecyclerViewAssembler private constructor(
private val interactionViewModelFactoryMap: Map<
String, @JvmSuppressWildcards InteractionViewModelFactory>,
@BackgroundDispatcher private val backgroundCoroutineDispatcher: CoroutineDispatcher,
- private val resourceHandler: AppLanguageResourceHandler
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val translationController: TranslationController
) {
/**
* Returns a new [Builder] for the specified GCS resource bucket information for loading
- * assets.
+ * assets, and the current logged in [ProfileId].
*/
- fun create(resourceBucketName: String, entityType: String): Builder {
+ fun create(resourceBucketName: String, entityType: String, profileId: ProfileId): Builder {
return Builder(
htmlParserFactory,
resourceBucketName,
entityType,
fragment,
+ profileId,
context,
interactionViewModelFactoryMap,
backgroundCoroutineDispatcher,
- resourceHandler
+ resourceHandler,
+ translationController
)
}
}
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 a59acca203a..dbabbbd32ee 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
@@ -2,6 +2,7 @@ package org.oppia.android.app.player.state.itemviewmodel
import org.oppia.android.app.model.InteractionObject
import org.oppia.android.app.model.UserAnswer
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver
import org.oppia.android.app.player.state.listener.PreviousNavigationButtonListener
@@ -21,21 +22,21 @@ class ContinueInteractionViewModel(
val hasConversationView: Boolean,
val hasPreviousButton: Boolean,
val previousNavigationButtonListener: PreviousNavigationButtonListener,
- val isSplitView: Boolean
+ val isSplitView: Boolean,
+ private val writtenTranslationContext: WrittenTranslationContext
) : StateItemViewModel(ViewType.CONTINUE_INTERACTION), InteractionAnswerHandler {
override fun isExplicitAnswerSubmissionRequired(): Boolean = false
override fun isAutoNavigating(): Boolean = true
- override fun getPendingAnswer(): UserAnswer {
- return UserAnswer.newBuilder()
- .setAnswer(
- InteractionObject.newBuilder().setNormalizedString(DEFAULT_CONTINUE_INTERACTION_TEXT_ANSWER)
- )
- .setPlainAnswer(DEFAULT_CONTINUE_INTERACTION_TEXT_ANSWER)
- .build()
- }
+ override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
+ answer = InteractionObject.newBuilder().apply {
+ normalizedString = DEFAULT_CONTINUE_INTERACTION_TEXT_ANSWER
+ }.build()
+ plainAnswer = DEFAULT_CONTINUE_INTERACTION_TEXT_ANSWER
+ this.writtenTranslationContext = this@ContinueInteractionViewModel.writtenTranslationContext
+ }.build()
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 fd8cb16eec7..6eb8dee40bc 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
@@ -12,12 +12,14 @@ import org.oppia.android.app.model.StringList
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.InteractionAnswerErrorOrAvailabilityCheckReceiver
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler
import org.oppia.android.app.recyclerview.BindableAdapter
import org.oppia.android.app.recyclerview.OnDragEndedListener
import org.oppia.android.app.recyclerview.OnItemDragListener
import org.oppia.android.app.translation.AppLanguageResourceHandler
+import org.oppia.android.domain.translation.TranslationController
/** [StateItemViewModel] for drag drop & sort choice list. */
class DragAndDropSortInteractionViewModel(
@@ -26,7 +28,9 @@ class DragAndDropSortInteractionViewModel(
interaction: Interaction,
private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length
val isSplitView: Boolean,
- private val resourceHandler: AppLanguageResourceHandler
+ private val writtenTranslationContext: WrittenTranslationContext,
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val translationController: TranslationController
) : StateItemViewModel(ViewType.DRAG_DROP_SORT_INTERACTION),
InteractionAnswerHandler,
OnItemDragListener,
@@ -44,7 +48,9 @@ class DragAndDropSortInteractionViewModel(
private val contentIdHtmlMap: Map =
choiceSubtitledHtmls.associate { subtitledHtml ->
- subtitledHtml.contentId to subtitledHtml.html
+ val translatedHtml =
+ translationController.extractString(subtitledHtml, writtenTranslationContext)
+ subtitledHtml.contentId to translatedHtml
}
private val _choiceItems: MutableList =
@@ -107,21 +113,19 @@ class DragAndDropSortInteractionViewModel(
(adapter as BindableAdapter<*>).setDataUnchecked(_choiceItems)
}
- override fun getPendingAnswer(): UserAnswer {
- val userAnswerBuilder = UserAnswer.newBuilder()
+ override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
val selectedLists = _choiceItems.map { it.htmlContent }
val userStringLists = _choiceItems.map { it.computeStringList() }
- userAnswerBuilder.listOfHtmlAnswers = convertItemsToAnswer(userStringLists)
- userAnswerBuilder.answer =
- InteractionObject.newBuilder().apply {
- listOfSetsOfTranslatableHtmlContentIds =
- ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply {
- _choiceItems.map { }
- addAllContentIdLists(selectedLists)
- }.build()
- }.build()
- return userAnswerBuilder.build()
- }
+ listOfHtmlAnswers = convertItemsToAnswer(userStringLists)
+ answer = InteractionObject.newBuilder().apply {
+ listOfSetsOfTranslatableHtmlContentIds =
+ ListOfSetsOfTranslatableHtmlContentIds.newBuilder().apply {
+ addAllContentIdLists(selectedLists)
+ }.build()
+ }.build()
+ this.writtenTranslationContext =
+ this@DragAndDropSortInteractionViewModel.writtenTranslationContext
+ }.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/FractionInteractionViewModel.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/FractionInteractionViewModel.kt
index aae7fe57621..b2e2329cefd 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
@@ -8,11 +8,13 @@ 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.UserAnswer
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.parser.StringToFractionParser
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.translation.AppLanguageResourceHandler
+import org.oppia.android.domain.translation.TranslationController
/** [StateItemViewModel] for the fraction input interaction. */
class FractionInteractionViewModel(
@@ -20,7 +22,9 @@ class FractionInteractionViewModel(
val hasConversationView: Boolean,
val isSplitView: Boolean,
private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver,
- private val resourceHandler: AppLanguageResourceHandler
+ private val writtenTranslationContext: WrittenTranslationContext,
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val translationController: TranslationController
) : StateItemViewModel(ViewType.FRACTION_INPUT_INTERACTION), InteractionAnswerHandler {
private var pendingAnswerError: String? = null
var answerText: CharSequence = ""
@@ -44,17 +48,16 @@ class FractionInteractionViewModel(
isAnswerAvailable.addOnPropertyChangedCallback(callback)
}
- override fun getPendingAnswer(): UserAnswer {
- val userAnswerBuilder = UserAnswer.newBuilder()
+ override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
if (answerText.isNotEmpty()) {
val answerTextString = answerText.toString()
- userAnswerBuilder.answer = InteractionObject.newBuilder()
- .setFraction(stringToFractionParser.parseFractionFromString(answerTextString))
- .build()
- userAnswerBuilder.plainAnswer = answerTextString
+ answer = InteractionObject.newBuilder().apply {
+ fraction = stringToFractionParser.parseFractionFromString(answerTextString)
+ }.build()
+ plainAnswer = answerTextString
+ this.writtenTranslationContext = this@FractionInteractionViewModel.writtenTranslationContext
}
- return userAnswerBuilder.build()
- }
+ }.build()
/** 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? {
@@ -94,12 +97,24 @@ class FractionInteractionViewModel(
}
private fun deriveHintText(interaction: Interaction): CharSequence {
- val customPlaceholder =
- interaction.customizationArgsMap["customPlaceholder"]?.subtitledUnicode?.unicodeStr ?: ""
+ // The subtitled unicode can apparently exist in the structure in two different formats.
+ val placeholderUnicodeOption1 =
+ interaction.customizationArgsMap["customPlaceholder"]?.subtitledUnicode
+ val placeholderUnicodeOption2 =
+ interaction.customizationArgsMap["customPlaceholder"]?.customSchemaValue?.subtitledUnicode
+ val customPlaceholder1 =
+ placeholderUnicodeOption1?.let { unicode ->
+ translationController.extractString(unicode, writtenTranslationContext)
+ } ?: ""
+ val customPlaceholder2 =
+ placeholderUnicodeOption2?.let { unicode ->
+ translationController.extractString(unicode, writtenTranslationContext)
+ } ?: ""
val allowNonzeroIntegerPart =
interaction.customizationArgsMap["allowNonzeroIntegerPart"]?.boolValue ?: true
return when {
- customPlaceholder.isNotEmpty() -> customPlaceholder
+ customPlaceholder1.isNotEmpty() -> customPlaceholder1
+ customPlaceholder2.isNotEmpty() -> customPlaceholder2
!allowNonzeroIntegerPart ->
resourceHandler.getStringInLocale(R.string.fractions_default_hint_text_no_integer)
else -> resourceHandler.getStringInLocale(R.string.fractions_default_hint_text)
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 fac1fe57326..7079d3b10b6 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.ImageWithRegions
import org.oppia.android.app.model.Interaction
import org.oppia.android.app.model.InteractionObject
import org.oppia.android.app.model.UserAnswer
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler
import org.oppia.android.app.translation.AppLanguageResourceHandler
@@ -23,6 +24,7 @@ class ImageRegionSelectionInteractionViewModel(
interaction: Interaction,
private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver,
val isSplitView: Boolean,
+ private val writtenTranslationContext: WrittenTranslationContext,
private val resourceHandler: AppLanguageResourceHandler
) : StateItemViewModel(ViewType.IMAGE_REGION_SELECTION_INTERACTION),
InteractionAnswerHandler,
@@ -66,17 +68,18 @@ class ImageRegionSelectionInteractionViewModel(
}
}
- override fun getPendingAnswer(): UserAnswer {
- val userAnswerBuilder = UserAnswer.newBuilder()
+ override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
val answerTextString = answerText.toString()
- userAnswerBuilder.answer =
- InteractionObject.newBuilder().setClickOnImage(parseClickOnImage(answerTextString)).build()
- userAnswerBuilder.plainAnswer = resourceHandler.getStringInLocaleWithWrapping(
+ answer = InteractionObject.newBuilder().apply {
+ clickOnImage = parseClickOnImage(answerTextString)
+ }.build()
+ plainAnswer = resourceHandler.getStringInLocaleWithWrapping(
R.string.image_interaction_answer_text,
answerTextString
)
- return userAnswerBuilder.build()
- }
+ this.writtenTranslationContext =
+ this@ImageRegionSelectionInteractionViewModel.writtenTranslationContext
+ }.build()
private fun parseClickOnImage(answerTextString: String): ClickOnImage {
val region = selectableRegions.find { it.label == answerTextString }
diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/InteractionViewModelFactory.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/InteractionViewModelFactory.kt
index 58196c76705..2ad060e78e3 100644
--- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/InteractionViewModelFactory.kt
+++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/InteractionViewModelFactory.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.WrittenTranslationContext
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver
@@ -16,5 +17,6 @@ typealias InteractionViewModelFactory = (
interactionAnswerReceiver: InteractionAnswerReceiver,
interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length
hasPreviousButton: Boolean,
- isSplitView: Boolean
+ isSplitView: Boolean,
+ writtenTranslationContext: WrittenTranslationContext
) -> StateItemViewModel
diff --git a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/InteractionViewModelModule.kt b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/InteractionViewModelModule.kt
index 59007fc6517..32f7d474d12 100644
--- a/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/InteractionViewModelModule.kt
+++ b/app/src/main/java/org/oppia/android/app/player/state/itemviewmodel/InteractionViewModelModule.kt
@@ -7,6 +7,7 @@ import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
import org.oppia.android.app.player.state.listener.PreviousNavigationButtonListener
import org.oppia.android.app.translation.AppLanguageResourceHandler
+import org.oppia.android.domain.translation.TranslationController
/**
* Module to provide interaction view model-specific dependencies for interactions that should be
@@ -25,13 +26,14 @@ class InteractionViewModelModule {
@StringKey("Continue")
fun provideContinueInteractionViewModelFactory(fragment: Fragment): InteractionViewModelFactory {
return { _, hasConversationView, _, interactionAnswerReceiver, _, hasPreviousButton,
- isSplitView ->
+ isSplitView, writtenTranslationContext ->
ContinueInteractionViewModel(
interactionAnswerReceiver,
hasConversationView,
hasPreviousButton,
fragment as PreviousNavigationButtonListener,
- isSplitView
+ isSplitView,
+ writtenTranslationContext
)
}
}
@@ -39,16 +41,20 @@ class InteractionViewModelModule {
@Provides
@IntoMap
@StringKey("MultipleChoiceInput")
- fun provideMultipleChoiceInputViewModelFactory(): InteractionViewModelFactory {
+ fun provideMultipleChoiceInputViewModelFactory(
+ translationController: TranslationController
+ ): InteractionViewModelFactory {
return { entityId, hasConversationView, interaction, interactionAnswerReceiver,
- interactionAnswerErrorReceiver, _, isSplitView ->
+ interactionAnswerErrorReceiver, _, isSplitView, writtenTranslationContext ->
SelectionInteractionViewModel(
entityId,
hasConversationView,
interaction,
interactionAnswerReceiver,
interactionAnswerErrorReceiver,
- isSplitView
+ isSplitView,
+ writtenTranslationContext,
+ translationController
)
}
}
@@ -56,16 +62,20 @@ class InteractionViewModelModule {
@Provides
@IntoMap
@StringKey("ItemSelectionInput")
- fun provideItemSelectionInputViewModelFactory(): InteractionViewModelFactory {
+ fun provideItemSelectionInputViewModelFactory(
+ translationController: TranslationController
+ ): InteractionViewModelFactory {
return { entityId, hasConversationView, interaction, interactionAnswerReceiver,
- interactionAnswerErrorReceiver, _, isSplitView ->
+ interactionAnswerErrorReceiver, _, isSplitView, writtenTranslationContext ->
SelectionInteractionViewModel(
entityId,
hasConversationView,
interaction,
interactionAnswerReceiver,
interactionAnswerErrorReceiver,
- isSplitView
+ isSplitView,
+ writtenTranslationContext,
+ translationController
)
}
}
@@ -74,16 +84,19 @@ class InteractionViewModelModule {
@IntoMap
@StringKey("FractionInput")
fun provideFractionInputViewModelFactory(
- resourceHandler: AppLanguageResourceHandler
+ resourceHandler: AppLanguageResourceHandler,
+ translationController: TranslationController
): InteractionViewModelFactory {
return { _, hasConversationView, interaction, _, interactionAnswerErrorReceiver, _,
- isSplitView ->
+ isSplitView, writtenTranslationContext ->
FractionInteractionViewModel(
interaction,
hasConversationView,
isSplitView,
interactionAnswerErrorReceiver,
- resourceHandler
+ writtenTranslationContext,
+ resourceHandler,
+ translationController
)
}
}
@@ -94,11 +107,13 @@ class InteractionViewModelModule {
fun provideNumericInputViewModelFactory(
resourceHandler: AppLanguageResourceHandler
): InteractionViewModelFactory {
- return { _, hasConversationView, _, _, interactionAnswerErrorReceiver, _, isSplitView ->
+ return { _, hasConversationView, _, _, interactionAnswerErrorReceiver, _, isSplitView,
+ writtenTranslationContext ->
NumericInputViewModel(
hasConversationView,
interactionAnswerErrorReceiver,
isSplitView,
+ writtenTranslationContext,
resourceHandler
)
}
@@ -107,11 +122,14 @@ class InteractionViewModelModule {
@Provides
@IntoMap
@StringKey("TextInput")
- fun provideTextInputViewModelFactory(): InteractionViewModelFactory {
+ fun provideTextInputViewModelFactory(
+ translationController: TranslationController
+ ): InteractionViewModelFactory {
return { _, hasConversationView, interaction, _, interactionAnswerErrorReceiver, _,
- isSplitView ->
+ isSplitView, writtenTranslationContext ->
TextInputViewModel(
- interaction, hasConversationView, interactionAnswerErrorReceiver, isSplitView
+ interaction, hasConversationView, interactionAnswerErrorReceiver, isSplitView,
+ writtenTranslationContext, translationController
)
}
}
@@ -120,13 +138,14 @@ class InteractionViewModelModule {
@IntoMap
@StringKey("DragAndDropSortInput")
fun provideDragAndDropSortInputViewModelFactory(
- resourceHandler: AppLanguageResourceHandler
+ resourceHandler: AppLanguageResourceHandler,
+ translationController: TranslationController
): InteractionViewModelFactory {
return { entityId, hasConversationView, interaction, _, interactionAnswerErrorReceiver, _,
- isSplitView ->
+ isSplitView, writtenTranslationContext ->
DragAndDropSortInteractionViewModel(
entityId, hasConversationView, interaction, interactionAnswerErrorReceiver, isSplitView,
- resourceHandler
+ writtenTranslationContext, resourceHandler, translationController
)
}
}
@@ -137,13 +156,15 @@ class InteractionViewModelModule {
fun provideImageClickInputViewModelFactory(
resourceHandler: AppLanguageResourceHandler
): InteractionViewModelFactory {
- return { entityId, hasConversationView, interaction, _, answerErrorReceiver, _, isSplitView ->
+ return { entityId, hasConversationView, interaction, _, answerErrorReceiver, _, isSplitView,
+ writtenTranslationContext ->
ImageRegionSelectionInteractionViewModel(
entityId,
hasConversationView,
interaction,
answerErrorReceiver,
isSplitView,
+ writtenTranslationContext,
resourceHandler
)
}
@@ -153,15 +174,19 @@ class InteractionViewModelModule {
@IntoMap
@StringKey("RatioExpressionInput")
fun provideRatioExpressionInputViewModelFactory(
- resourceHandler: AppLanguageResourceHandler
+ resourceHandler: AppLanguageResourceHandler,
+ translationController: TranslationController
): InteractionViewModelFactory {
- return { _, hasConversationView, interaction, _, answerErrorReceiver, _, isSplitView ->
+ return { _, hasConversationView, interaction, _, answerErrorReceiver, _, isSplitView,
+ writtenTranslationContext ->
RatioExpressionInputInteractionViewModel(
interaction,
hasConversationView,
isSplitView,
answerErrorReceiver,
- resourceHandler
+ writtenTranslationContext,
+ resourceHandler,
+ translationController
)
}
}
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 01bc68eca04..4c34453e937 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.InteractionObject
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
@@ -17,6 +18,7 @@ class NumericInputViewModel(
val hasConversationView: Boolean,
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 = ""
@@ -74,14 +76,14 @@ class NumericInputViewModel(
}
}
- override fun getPendingAnswer(): UserAnswer {
- val userAnswerBuilder = UserAnswer.newBuilder()
+ override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
if (answerText.isNotEmpty()) {
val answerTextString = answerText.toString()
- userAnswerBuilder.answer =
- InteractionObject.newBuilder().setReal(answerTextString.toDouble()).build()
- userAnswerBuilder.plainAnswer = answerTextString
+ answer = InteractionObject.newBuilder().apply {
+ real = answerTextString.toDouble()
+ }.build()
+ plainAnswer = answerTextString
+ this.writtenTranslationContext = this@NumericInputViewModel.writtenTranslationContext
}
- return userAnswerBuilder.build()
- }
+ }.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 768ff05f560..064b7fc3f60 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
@@ -8,12 +8,14 @@ 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.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.translation.AppLanguageResourceHandler
import org.oppia.android.app.utility.toAccessibleAnswerString
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.util.toAnswerString
/** [StateItemViewModel] for the ratio expression input interaction. */
@@ -22,7 +24,9 @@ class RatioExpressionInputInteractionViewModel(
val hasConversationView: Boolean,
val isSplitView: Boolean,
private val errorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver,
- private val resourceHandler: AppLanguageResourceHandler
+ private val writtenTranslationContext: WrittenTranslationContext,
+ private val resourceHandler: AppLanguageResourceHandler,
+ private val translationController: TranslationController
) : StateItemViewModel(ViewType.RATIO_EXPRESSION_INPUT_INTERACTION), InteractionAnswerHandler {
private var pendingAnswerError: String? = null
var answerText: CharSequence = ""
@@ -48,18 +52,18 @@ class RatioExpressionInputInteractionViewModel(
isAnswerAvailable.addOnPropertyChangedCallback(callback)
}
- override fun getPendingAnswer(): UserAnswer {
- val userAnswerBuilder = UserAnswer.newBuilder()
+ override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
if (answerText.isNotEmpty()) {
val ratioAnswer = stringToRatioParser.parseRatioOrThrow(answerText.toString())
- userAnswerBuilder.answer = InteractionObject.newBuilder()
- .setRatioExpression(ratioAnswer)
- .build()
- userAnswerBuilder.plainAnswer = ratioAnswer.toAnswerString()
- userAnswerBuilder.contentDescription = ratioAnswer.toAccessibleAnswerString(resourceHandler)
+ answer = InteractionObject.newBuilder().apply {
+ ratioExpression = ratioAnswer
+ }.build()
+ plainAnswer = ratioAnswer.toAnswerString()
+ contentDescription = ratioAnswer.toAccessibleAnswerString(resourceHandler)
+ this.writtenTranslationContext =
+ this@RatioExpressionInputInteractionViewModel.writtenTranslationContext
}
- return userAnswerBuilder.build()
- }
+ }.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? {
@@ -101,10 +105,22 @@ class RatioExpressionInputInteractionViewModel(
}
private fun deriveHintText(interaction: Interaction): CharSequence {
- val placeholder =
- interaction.customizationArgsMap["placeholder"]?.subtitledUnicode?.unicodeStr ?: ""
+ // The subtitled unicode can apparently exist in the structure in two different formats.
+ val placeholderUnicodeOption1 =
+ interaction.customizationArgsMap["placeholder"]?.subtitledUnicode
+ val placeholderUnicodeOption2 =
+ interaction.customizationArgsMap["placeholder"]?.customSchemaValue?.subtitledUnicode
+ val placeholder1 =
+ placeholderUnicodeOption1?.let { unicode ->
+ translationController.extractString(unicode, writtenTranslationContext)
+ } ?: ""
+ val placeholder2 =
+ placeholderUnicodeOption2?.let { unicode ->
+ translationController.extractString(unicode, writtenTranslationContext)
+ } ?: ""
return when {
- placeholder.isNotEmpty() -> placeholder
+ placeholder1.isNotEmpty() -> placeholder1
+ placeholder2.isNotEmpty() -> placeholder2
else -> resourceHandler.getStringInLocale(R.string.ratio_default_hint_text)
}
}
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 9a26bac0524..1254f9fbfb5 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
@@ -9,10 +9,12 @@ import org.oppia.android.app.model.SetOfTranslatableHtmlContentIds
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.InteractionAnswerErrorOrAvailabilityCheckReceiver
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerReceiver
import org.oppia.android.app.viewmodel.ObservableArrayList
+import org.oppia.android.domain.translation.TranslationController
/** Corresponds to the type of input that should be used for an item selection interaction view. */
enum class SelectionItemInputType {
@@ -27,7 +29,9 @@ class SelectionInteractionViewModel(
interaction: Interaction,
private val interactionAnswerReceiver: InteractionAnswerReceiver,
private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length
- val isSplitView: Boolean
+ val isSplitView: Boolean,
+ val writtenTranslationContext: WrittenTranslationContext,
+ private val translationController: TranslationController
) : StateItemViewModel(ViewType.SELECTION_INTERACTION), InteractionAnswerHandler {
private val interactionId: String = interaction.id
@@ -71,11 +75,14 @@ class SelectionInteractionViewModel(
return maxAllowableSelectionCount > 1
}
- override fun getPendingAnswer(): UserAnswer {
- val userAnswerBuilder = UserAnswer.newBuilder()
+ override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
+ val translationContext = this@SelectionInteractionViewModel.writtenTranslationContext
val selectedItemSubtitledHtmls = selectedItems.map(choiceItems::get).map { it.htmlContent }
+ val itemHtmls = selectedItemSubtitledHtmls.map { subtitledHtml ->
+ translationController.extractString(subtitledHtml, translationContext)
+ }
if (interactionId == "ItemSelectionInput") {
- userAnswerBuilder.answer = InteractionObject.newBuilder().apply {
+ answer = InteractionObject.newBuilder().apply {
setOfTranslatableHtmlContentIds = SetOfTranslatableHtmlContentIds.newBuilder().apply {
addAllContentIds(
selectedItemSubtitledHtmls.map { subtitledHtml ->
@@ -86,23 +93,23 @@ class SelectionInteractionViewModel(
)
}.build()
}.build()
- userAnswerBuilder.htmlAnswer = convertSelectedItemsToHtmlString(selectedItemSubtitledHtmls)
+ htmlAnswer = convertSelectedItemsToHtmlString(itemHtmls)
} else if (selectedItems.size == 1) {
- userAnswerBuilder.answer =
- InteractionObject.newBuilder().setNonNegativeInt(selectedItems.first()).build()
- userAnswerBuilder.htmlAnswer = convertSelectedItemsToHtmlString(selectedItemSubtitledHtmls)
+ answer = InteractionObject.newBuilder().apply {
+ nonNegativeInt = selectedItems.first()
+ }.build()
+ htmlAnswer = convertSelectedItemsToHtmlString(itemHtmls)
}
- return userAnswerBuilder.build()
- }
+ writtenTranslationContext = translationContext
+ }.build()
/** Returns an HTML list containing all of the HTML string elements as items in the list. */
- private fun convertSelectedItemsToHtmlString(subtitledHtmls: Collection): String {
- return when (subtitledHtmls.size) {
+ private fun convertSelectedItemsToHtmlString(itemHtmls: Collection): String {
+ return when (itemHtmls.size) {
0 -> ""
- 1 -> subtitledHtmls.first().html
+ 1 -> itemHtmls.first()
else -> {
- val htmlList = subtitledHtmls.map { it.html }
- "${htmlList.joinToString(separator = " ")} "
+ "${itemHtmls.joinToString(separator = " ")} "
}
}
}
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 db5e2711d60..821faa8676d 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,15 +7,19 @@ import androidx.databinding.ObservableField
import org.oppia.android.app.model.Interaction
import org.oppia.android.app.model.InteractionObject
import org.oppia.android.app.model.UserAnswer
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerErrorOrAvailabilityCheckReceiver
import org.oppia.android.app.player.state.answerhandling.InteractionAnswerHandler
+import org.oppia.android.domain.translation.TranslationController
/** [StateItemViewModel] for the text input interaction. */
class TextInputViewModel(
interaction: Interaction,
val hasConversationView: Boolean,
private val interactionAnswerErrorOrAvailabilityCheckReceiver: InteractionAnswerErrorOrAvailabilityCheckReceiver, // ktlint-disable max-line-length
- val isSplitView: Boolean
+ val isSplitView: Boolean,
+ private val writtenTranslationContext: WrittenTranslationContext,
+ private val translationController: TranslationController
) : StateItemViewModel(ViewType.TEXT_INPUT_INTERACTION), InteractionAnswerHandler {
var answerText: CharSequence = ""
val hintText: CharSequence = deriveHintText(interaction)
@@ -53,19 +57,31 @@ class TextInputViewModel(
}
}
- override fun getPendingAnswer(): UserAnswer {
- val userAnswerBuilder = UserAnswer.newBuilder()
+ override fun getPendingAnswer(): UserAnswer = UserAnswer.newBuilder().apply {
if (answerText.isNotEmpty()) {
val answerTextString = answerText.toString()
- userAnswerBuilder.answer =
- InteractionObject.newBuilder().setNormalizedString(answerTextString).build()
- userAnswerBuilder.plainAnswer = answerTextString
+ answer = InteractionObject.newBuilder().apply {
+ normalizedString = answerTextString
+ }.build()
+ plainAnswer = answerTextString
+ writtenTranslationContext = this@TextInputViewModel.writtenTranslationContext
}
- return userAnswerBuilder.build()
- }
+ }.build()
private fun deriveHintText(interaction: Interaction): CharSequence {
- // The default placeholder for text input is empty.
- return interaction.customizationArgsMap["placeholder"]?.subtitledUnicode?.unicodeStr ?: ""
+ // The subtitled unicode can apparently exist in the structure in two different formats.
+ val placeholderUnicodeOption1 =
+ interaction.customizationArgsMap["placeholder"]?.subtitledUnicode
+ val placeholderUnicodeOption2 =
+ interaction.customizationArgsMap["placeholder"]?.customSchemaValue?.subtitledUnicode
+ val placeholder1 =
+ placeholderUnicodeOption1?.let { unicode ->
+ translationController.extractString(unicode, writtenTranslationContext)
+ } ?: ""
+ val placeholder2 =
+ placeholderUnicodeOption2?.let { unicode ->
+ translationController.extractString(unicode, writtenTranslationContext)
+ } ?: "" // The default placeholder for text input is empty.
+ return if (placeholder1.isNotEmpty()) placeholder1 else placeholder2
}
}
diff --git a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivity.kt b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivity.kt
index 7a02f1fde96..8f677ae302e 100644
--- a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivity.kt
@@ -11,6 +11,7 @@ import org.oppia.android.app.hintsandsolution.RevealHintListener
import org.oppia.android.app.hintsandsolution.RevealSolutionInterface
import org.oppia.android.app.model.HelpIndex
import org.oppia.android.app.model.State
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.audio.AudioButtonListener
import org.oppia.android.app.player.exploration.HintsAndSolutionExplorationManagerListener
import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_DIALOG
@@ -44,6 +45,7 @@ class StateFragmentTestActivity :
@Inject
lateinit var stateFragmentTestActivityPresenter: StateFragmentTestActivityPresenter
private lateinit var state: State
+ private lateinit var writtenTranslationContext: WrittenTranslationContext
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -106,7 +108,8 @@ class StateFragmentTestActivity :
HintsAndSolutionDialogFragment.newInstance(
explorationId,
state,
- helpIndex
+ helpIndex,
+ writtenTranslationContext
)
hintsAndSolutionFragment.showNow(supportFragmentManager, TAG_HINTS_AND_SOLUTION_DIALOG)
}
@@ -120,8 +123,12 @@ class StateFragmentTestActivity :
stateFragmentTestActivityPresenter.revealSolution()
}
- override fun onExplorationStateLoaded(state: State) {
+ override fun onExplorationStateLoaded(
+ state: State,
+ writtenTranslationContext: WrittenTranslationContext
+ ) {
this.state = state
+ this.writtenTranslationContext = writtenTranslationContext
}
private fun getHintsAndSolution(): HintsAndSolutionDialogFragment? {
diff --git a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt
index 92fbf4c80eb..3ca8d4771b4 100644
--- a/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/player/state/testing/StateFragmentTestActivityPresenter.kt
@@ -61,14 +61,6 @@ class StateFragmentTestActivityPresenter @Inject constructor(
activity.findViewById(R.id.play_test_exploration_button)?.setOnClickListener {
startPlayingExploration(profileId, topicId, storyId, explorationId, shouldSavePartialProgress)
}
-
- if (getHintsAndSolutionManagerFragment() == null) {
- activity.supportFragmentManager.beginTransaction().add(
- R.id.exploration_fragment_placeholder,
- HintsAndSolutionExplorationManagerFragment(),
- TAG_HINTS_AND_SOLUTION_EXPLORATION_MANAGER
- ).commitNow()
- }
}
fun stopExploration() = finishExploration()
@@ -124,6 +116,10 @@ class StateFragmentTestActivityPresenter @Inject constructor(
)
}
+ /**
+ * Initializes fragments that depend on ephemeral state (which isn't valid to do until the play
+ * session is fully started).
+ */
private fun initializeExploration(
profileId: Int,
topicId: String,
@@ -137,6 +133,14 @@ class StateFragmentTestActivityPresenter @Inject constructor(
R.id.state_fragment_placeholder,
stateFragment
).commitNow()
+
+ if (getHintsAndSolutionManagerFragment() == null) {
+ activity.supportFragmentManager.beginTransaction().add(
+ R.id.exploration_fragment_placeholder,
+ HintsAndSolutionExplorationManagerFragment(),
+ TAG_HINTS_AND_SOLUTION_EXPLORATION_MANAGER
+ ).commitNow()
+ }
}
private fun finishExploration() {
diff --git a/app/src/main/java/org/oppia/android/app/shim/ViewBindingShim.kt b/app/src/main/java/org/oppia/android/app/shim/ViewBindingShim.kt
index 4356120a55f..cb57334b06d 100644
--- a/app/src/main/java/org/oppia/android/app/shim/ViewBindingShim.kt
+++ b/app/src/main/java/org/oppia/android/app/shim/ViewBindingShim.kt
@@ -9,6 +9,7 @@ import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView
import org.oppia.android.app.home.promotedlist.ComingSoonTopicsViewModel
import org.oppia.android.app.home.promotedlist.PromotedStoryViewModel
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.state.itemviewmodel.DragDropInteractionContentViewModel
import org.oppia.android.app.player.state.itemviewmodel.SelectionInteractionContentViewModel
import org.oppia.android.util.parser.html.HtmlParser
@@ -139,7 +140,8 @@ interface ViewBindingShim {
htmlParserFactory: HtmlParser.Factory,
resourceBucketName: String,
entityType: String,
- entityId: String
+ entityId: String,
+ writtenTranslationContext: WrittenTranslationContext
)
/**
@@ -162,6 +164,7 @@ interface ViewBindingShim {
htmlParserFactory: HtmlParser.Factory,
resourceBucketName: String,
entityType: String,
- entityId: String
+ entityId: String,
+ writtenTranslationContext: WrittenTranslationContext
)
}
diff --git a/app/src/main/java/org/oppia/android/app/shim/ViewBindingShimImpl.kt b/app/src/main/java/org/oppia/android/app/shim/ViewBindingShimImpl.kt
index a1545be5de6..fcb28a4b22e 100644
--- a/app/src/main/java/org/oppia/android/app/shim/ViewBindingShimImpl.kt
+++ b/app/src/main/java/org/oppia/android/app/shim/ViewBindingShimImpl.kt
@@ -11,6 +11,7 @@ import androidx.recyclerview.widget.RecyclerView
import org.oppia.android.R
import org.oppia.android.app.home.promotedlist.ComingSoonTopicsViewModel
import org.oppia.android.app.home.promotedlist.PromotedStoryViewModel
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.state.itemviewmodel.DragDropInteractionContentViewModel
import org.oppia.android.app.player.state.itemviewmodel.SelectionInteractionContentViewModel
import org.oppia.android.databinding.ComingSoonTopicViewBinding
@@ -19,6 +20,7 @@ import org.oppia.android.databinding.DragDropSingleItemBinding
import org.oppia.android.databinding.ItemSelectionInteractionItemsBinding
import org.oppia.android.databinding.MultipleChoiceInteractionItemsBinding
import org.oppia.android.databinding.PromotedStoryCardBinding
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.util.parser.html.HtmlParser
import javax.inject.Inject
@@ -31,7 +33,9 @@ import javax.inject.Inject
* View once Gradle has been removed.
*/
// TODO(#1619): Remove file post-Gradle
-class ViewBindingShimImpl @Inject constructor() : ViewBindingShim {
+class ViewBindingShimImpl @Inject constructor(
+ private val translationController: TranslationController
+) : ViewBindingShim {
override fun providePromotedStoryCardInflatedView(
inflater: LayoutInflater,
@@ -89,7 +93,8 @@ class ViewBindingShimImpl @Inject constructor() : ViewBindingShim {
htmlParserFactory: HtmlParser.Factory,
resourceBucketName: String,
entityType: String,
- entityId: String
+ entityId: String,
+ writtenTranslationContext: WrittenTranslationContext
) {
val binding =
DataBindingUtil.findBinding(view)!!
@@ -100,7 +105,7 @@ class ViewBindingShimImpl @Inject constructor() : ViewBindingShim {
entityId,
false
).parseOppiaHtml(
- viewModel.htmlContent.html,
+ translationController.extractString(viewModel.htmlContent, writtenTranslationContext),
binding.itemSelectionContentsTextView
)
binding.viewModel = viewModel
@@ -124,7 +129,8 @@ class ViewBindingShimImpl @Inject constructor() : ViewBindingShim {
htmlParserFactory: HtmlParser.Factory,
resourceBucketName: String,
entityType: String,
- entityId: String
+ entityId: String,
+ writtenTranslationContext: WrittenTranslationContext
) {
val binding =
DataBindingUtil.findBinding(view)!!
@@ -132,7 +138,8 @@ class ViewBindingShimImpl @Inject constructor() : ViewBindingShim {
htmlParserFactory.create(
resourceBucketName, entityType, entityId, /* imageCenterAlign= */ false
).parseOppiaHtml(
- viewModel.htmlContent.html, binding.multipleChoiceContentTextView
+ translationController.extractString(viewModel.htmlContent, writtenTranslationContext),
+ binding.multipleChoiceContentTextView
)
binding.viewModel = viewModel
}
diff --git a/app/src/main/java/org/oppia/android/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt b/app/src/main/java/org/oppia/android/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt
index 7a3682a7383..67ebaaa024f 100644
--- a/app/src/main/java/org/oppia/android/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/story/storyitemviewmodel/StoryChapterSummaryViewModel.kt
@@ -45,7 +45,9 @@ class StoryChapterSummaryViewModel(
if (chapterPlayState == ChapterPlayState.IN_PROGRESS_SAVED) {
val explorationCheckpointLiveData =
explorationCheckpointController.retrieveExplorationCheckpoint(
- ProfileId.getDefaultInstance(),
+ ProfileId.newBuilder().apply {
+ internalId = internalProfileId
+ }.build(),
explorationId
).toLiveData()
diff --git a/app/src/main/java/org/oppia/android/app/testing/ConceptCardFragmentTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/ConceptCardFragmentTestActivity.kt
index 119ebbd9bf6..e1c458a179e 100644
--- a/app/src/main/java/org/oppia/android/app/testing/ConceptCardFragmentTestActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/testing/ConceptCardFragmentTestActivity.kt
@@ -1,10 +1,15 @@
package org.oppia.android.app.testing
+import android.content.Context
+import android.content.Intent
import android.os.Bundle
import org.oppia.android.app.activity.ActivityComponentImpl
import org.oppia.android.app.activity.InjectableAppCompatActivity
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.topic.conceptcard.ConceptCardFragment
import org.oppia.android.app.topic.conceptcard.ConceptCardListener
+import org.oppia.android.util.extensions.getProtoExtra
+import org.oppia.android.util.extensions.putProtoExtra
import javax.inject.Inject
/** Test Activity used for testing ConceptCardFragment */
@@ -16,7 +21,9 @@ class ConceptCardFragmentTestActivity : InjectableAppCompatActivity(), ConceptCa
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activityComponent as ActivityComponentImpl).inject(this)
- conceptCardFragmentTestActivityController.handleOnCreate()
+ conceptCardFragmentTestActivityController.handleOnCreate(
+ intent.getProtoExtra(TEST_ACTIVITY_PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance())
+ )
}
override fun dismissConceptCard() {
@@ -28,6 +35,15 @@ class ConceptCardFragmentTestActivity : InjectableAppCompatActivity(), ConceptCa
}
companion object {
+ private const val TEST_ACTIVITY_PROFILE_ID_ARGUMENT_KEY =
+ "ConceptCardFragmentTestActivity.profile_id"
+
internal const val TAG_CONCEPT_CARD_DIALOG = "CONCEPT_CARD_DIALOG"
+
+ fun createIntent(context: Context, profileId: ProfileId): Intent {
+ return Intent(context, ConceptCardFragmentTestActivity::class.java).also {
+ it.putProtoExtra(TEST_ACTIVITY_PROFILE_ID_ARGUMENT_KEY, profileId)
+ }
+ }
}
}
diff --git a/app/src/main/java/org/oppia/android/app/testing/ConceptCardFragmentTestActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/testing/ConceptCardFragmentTestActivityPresenter.kt
index f64cfbd2a69..6738e55759f 100644
--- a/app/src/main/java/org/oppia/android/app/testing/ConceptCardFragmentTestActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/testing/ConceptCardFragmentTestActivityPresenter.kt
@@ -3,6 +3,7 @@ package org.oppia.android.app.testing
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import org.oppia.android.R
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.testing.ConceptCardFragmentTestActivity.Companion.TAG_CONCEPT_CARD_DIALOG
import org.oppia.android.app.topic.conceptcard.ConceptCardFragment
import org.oppia.android.domain.topic.TEST_SKILL_ID_0
@@ -13,14 +14,14 @@ import javax.inject.Inject
class ConceptCardFragmentTestActivityPresenter @Inject constructor(
private val activity: AppCompatActivity
) {
- fun handleOnCreate() {
+ fun handleOnCreate(profileId: ProfileId) {
activity.setContentView(R.layout.concept_card_fragment_test_activity)
activity.findViewById(R.id.open_dialog_0).setOnClickListener {
- val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_0)
+ val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_0, profileId)
frag.showNow(activity.supportFragmentManager, TAG_CONCEPT_CARD_DIALOG)
}
activity.findViewById(R.id.open_dialog_1).setOnClickListener {
- val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_1)
+ val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_1, profileId)
frag.showNow(activity.supportFragmentManager, TAG_CONCEPT_CARD_DIALOG)
}
}
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 777ff295daf..327955f0075 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.customview.interaction.NumericInputInteractionView
import org.oppia.android.app.customview.interaction.TextInputInteractionView
import org.oppia.android.app.model.Interaction
import org.oppia.android.app.model.SchemaObject
+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.itemviewmodel.FractionInteractionViewModel
@@ -20,6 +21,7 @@ import org.oppia.android.app.player.state.itemviewmodel.TextInputViewModel
import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener
import org.oppia.android.app.translation.AppLanguageResourceHandler
import org.oppia.android.databinding.ActivityInputInteractionViewTestBinding
+import org.oppia.android.domain.translation.TranslationController
import javax.inject.Inject
/**
@@ -37,11 +39,15 @@ class InputInteractionViewTestActivity :
@Inject
lateinit var resourceHandler: AppLanguageResourceHandler
+ @Inject
+ lateinit var translationController: TranslationController
+
val numericInputViewModel by lazy {
NumericInputViewModel(
hasConversationView = false,
interactionAnswerErrorOrAvailabilityCheckReceiver = this,
isSplitView = false,
+ writtenTranslationContext = WrittenTranslationContext.getDefaultInstance(),
resourceHandler = resourceHandler
)
}
@@ -51,7 +57,9 @@ class InputInteractionViewTestActivity :
interaction = Interaction.getDefaultInstance(),
hasConversationView = false,
interactionAnswerErrorOrAvailabilityCheckReceiver = this,
- isSplitView = false
+ isSplitView = false,
+ writtenTranslationContext = WrittenTranslationContext.getDefaultInstance(),
+ translationController = translationController
)
}
@@ -66,7 +74,9 @@ class InputInteractionViewTestActivity :
hasConversationView = false,
isSplitView = false,
errorOrAvailabilityCheckReceiver = this,
- resourceHandler = resourceHandler
+ writtenTranslationContext = WrittenTranslationContext.getDefaultInstance(),
+ resourceHandler = resourceHandler,
+ translationController = translationController
)
ratioExpressionInputInteractionViewModel = RatioExpressionInputInteractionViewModel(
@@ -77,7 +87,9 @@ class InputInteractionViewTestActivity :
hasConversationView = false,
isSplitView = false,
errorOrAvailabilityCheckReceiver = this,
- resourceHandler = resourceHandler
+ writtenTranslationContext = WrittenTranslationContext.getDefaultInstance(),
+ resourceHandler = resourceHandler,
+ translationController = translationController
)
binding.numericInputViewModel = numericInputViewModel
binding.textInputViewModel = textInputViewModel
diff --git a/app/src/main/java/org/oppia/android/app/testing/TopicTestActivity.kt b/app/src/main/java/org/oppia/android/app/testing/TopicTestActivity.kt
index 9c1e1ead1ad..eaa6c7a54af 100644
--- a/app/src/main/java/org/oppia/android/app/testing/TopicTestActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/testing/TopicTestActivity.kt
@@ -4,6 +4,7 @@ import android.os.Bundle
import org.oppia.android.app.activity.ActivityComponentImpl
import org.oppia.android.app.activity.InjectableAppCompatActivity
import org.oppia.android.app.home.RouteToExplorationListener
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.player.exploration.ExplorationActivity
import org.oppia.android.app.story.StoryActivity
import org.oppia.android.app.topic.RouteToQuestionPlayerListener
@@ -40,7 +41,7 @@ class TopicTestActivity :
override fun routeToQuestionPlayer(skillIdList: ArrayList) {
startActivity(
QuestionPlayerActivity.createQuestionPlayerActivityIntent(
- this, skillIdList
+ this, skillIdList, ProfileId.getDefaultInstance()
)
)
}
diff --git a/app/src/main/java/org/oppia/android/app/testing/TopicTestActivityForStory.kt b/app/src/main/java/org/oppia/android/app/testing/TopicTestActivityForStory.kt
index 82b7eb7aebc..b055504b828 100644
--- a/app/src/main/java/org/oppia/android/app/testing/TopicTestActivityForStory.kt
+++ b/app/src/main/java/org/oppia/android/app/testing/TopicTestActivityForStory.kt
@@ -5,6 +5,7 @@ import org.oppia.android.app.activity.ActivityComponentImpl
import org.oppia.android.app.activity.InjectableAppCompatActivity
import org.oppia.android.app.home.RouteToExplorationListener
import org.oppia.android.app.model.ExplorationCheckpoint
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.player.exploration.ExplorationActivity
import org.oppia.android.app.resumelesson.ResumeLessonActivity
import org.oppia.android.app.story.StoryActivity
@@ -45,7 +46,7 @@ class TopicTestActivityForStory :
override fun routeToQuestionPlayer(skillIdList: ArrayList) {
startActivity(
QuestionPlayerActivity.createQuestionPlayerActivityIntent(
- this, skillIdList
+ this, skillIdList, ProfileId.getDefaultInstance()
)
)
}
diff --git a/app/src/main/java/org/oppia/android/app/topic/TopicActivity.kt b/app/src/main/java/org/oppia/android/app/topic/TopicActivity.kt
index 41a95b0ea04..d3991d3edf8 100755
--- a/app/src/main/java/org/oppia/android/app/topic/TopicActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/TopicActivity.kt
@@ -52,7 +52,8 @@ class TopicActivity :
startActivity(
QuestionPlayerActivity.createQuestionPlayerActivityIntent(
this,
- skillIdList
+ skillIdList,
+ ProfileId.newBuilder().setInternalId(internalProfileId).build()
)
)
}
diff --git a/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragment.kt b/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragment.kt
index b6f7c8e6bb9..bdbb9bdca99 100644
--- a/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragment.kt
@@ -8,10 +8,14 @@ import android.view.ViewGroup
import org.oppia.android.R
import org.oppia.android.app.fragment.FragmentComponentImpl
import org.oppia.android.app.fragment.InjectableDialogFragment
+import org.oppia.android.app.model.ProfileId
+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
private const val SKILL_ID_ARGUMENT_KEY = "ConceptCardFragment.skill_id"
+private const val PROFILE_ID_ARGUMENT_KEY = "ConceptCardFragment.profile_id"
/* Fragment that displays a fullscreen dialog for concept cards */
class ConceptCardFragment : InjectableDialogFragment() {
@@ -21,16 +25,19 @@ class ConceptCardFragment : InjectableDialogFragment() {
const val CONCEPT_CARD_DIALOG_FRAGMENT_TAG = "CONCEPT_CARD_FRAGMENT"
/**
- * Creates a new instance of a DialogFragment to display content
- * @param skillId Used in TopicController to get correct concept card data.
- * @return [ConceptCardFragment]: DialogFragment
+ * Creates a new fragment to show a concept card.
+ *
+ * @param skillId the skill ID for which a concept card should be loaded
+ * @param profileId the profile in which the concept card will be shown
+ * @return a new [ConceptCardFragment] to display the specified concept card
*/
- fun newInstance(skillId: String): ConceptCardFragment {
- val conceptCardFrag = ConceptCardFragment()
- val args = Bundle()
- args.putString(SKILL_ID_ARGUMENT_KEY, skillId)
- conceptCardFrag.arguments = args
- return conceptCardFrag
+ fun newInstance(skillId: String, profileId: ProfileId): ConceptCardFragment {
+ return ConceptCardFragment().apply {
+ arguments = Bundle().apply {
+ putString(SKILL_ID_ARGUMENT_KEY, skillId)
+ putProto(PROFILE_ID_ARGUMENT_KEY, profileId)
+ }
+ }
}
}
@@ -60,7 +67,8 @@ class ConceptCardFragment : InjectableDialogFragment() {
checkNotNull(args.getStringFromBundle(SKILL_ID_ARGUMENT_KEY)) {
"Expected skillId to be passed to ConceptCardFragment"
}
- return conceptCardFragmentPresenter.handleCreateView(inflater, container, skillId)
+ val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance())
+ return conceptCardFragmentPresenter.handleCreateView(inflater, container, skillId, profileId)
}
override fun onStart() {
diff --git a/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentPresenter.kt
index c05d94f74bb..087c927f560 100644
--- a/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentPresenter.kt
@@ -4,13 +4,14 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
import org.oppia.android.R
import org.oppia.android.app.fragment.FragmentScope
import org.oppia.android.app.model.EventLog
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.viewmodel.ViewModelProvider
import org.oppia.android.databinding.ConceptCardFragmentBinding
import org.oppia.android.domain.oppialogger.OppiaLogger
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.util.gcsresource.DefaultResourceBucketName
import org.oppia.android.util.parser.html.ConceptCardHtmlParserEntityType
import org.oppia.android.util.parser.html.HtmlParser
@@ -26,15 +27,19 @@ class ConceptCardFragmentPresenter @Inject constructor(
private val htmlParserFactory: HtmlParser.Factory,
@ConceptCardHtmlParserEntityType private val entityType: String,
@DefaultResourceBucketName private val resourceBucketName: String,
- private val viewModelProvider: ViewModelProvider
+ private val viewModelProvider: ViewModelProvider,
+ private val translationController: TranslationController
) {
- private lateinit var skillId: String
-
/**
* Sets up data binding and toolbar.
* Host activity must inherit ConceptCardListener to dismiss this fragment.
*/
- fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?, id: String): View? {
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ skillId: String,
+ profileId: ProfileId
+ ): View? {
val binding = ConceptCardFragmentBinding.inflate(
inflater,
container,
@@ -43,8 +48,7 @@ class ConceptCardFragmentPresenter @Inject constructor(
val view = binding.conceptCardExplanationText
val viewModel = getConceptCardViewModel()
- skillId = id
- viewModel.setSkillId(skillId)
+ viewModel.initialize(skillId, profileId)
logConceptCardEvent(skillId)
binding.conceptCardToolbar.setNavigationIcon(R.drawable.ic_close_white_24dp)
@@ -62,10 +66,15 @@ class ConceptCardFragmentPresenter @Inject constructor(
viewModel.conceptCardLiveData.observe(
fragment,
- Observer {
+ { ephemeralConceptCard ->
+ val explanationHtml =
+ translationController.extractString(
+ ephemeralConceptCard.conceptCard.explanation,
+ ephemeralConceptCard.writtenTranslationContext
+ )
view.text = htmlParserFactory
.create(resourceBucketName, entityType, skillId, imageCenterAlign = true)
- .parseOppiaHtml(it.explanation.html, view)
+ .parseOppiaHtml(explanationHtml, view)
}
)
diff --git a/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardViewModel.kt b/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardViewModel.kt
index 217db3f5ff6..f360fa5c359 100644
--- a/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/conceptcard/ConceptCardViewModel.kt
@@ -3,11 +3,13 @@ package org.oppia.android.app.topic.conceptcard
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import org.oppia.android.app.fragment.FragmentScope
-import org.oppia.android.app.model.ConceptCard
+import org.oppia.android.app.model.EphemeralConceptCard
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.viewmodel.ObservableViewModel
import org.oppia.android.domain.oppialogger.OppiaLogger
import org.oppia.android.domain.topic.TopicController
import org.oppia.android.util.data.AsyncResult
+import org.oppia.android.util.data.DataProviders.Companion.toLiveData
import javax.inject.Inject
/** [ObservableViewModel] for concept card, providing rich text and worked examples */
@@ -17,24 +19,28 @@ class ConceptCardViewModel @Inject constructor(
private val oppiaLogger: OppiaLogger
) : ObservableViewModel() {
private lateinit var skillId: String
+ private lateinit var profileId: ProfileId
- val conceptCardLiveData: LiveData by lazy {
+ val conceptCardLiveData: LiveData by lazy {
processConceptCardLiveData()
}
- fun setSkillId(id: String) {
- skillId = id
+ fun initialize(skillId: String, profileId: ProfileId) {
+ this.skillId = skillId
+ this.profileId = profileId
}
- private val conceptCardResultLiveData: LiveData> by lazy {
- topicController.getConceptCard(skillId)
+ private val conceptCardResultLiveData: LiveData> by lazy {
+ topicController.getConceptCard(profileId, skillId).toLiveData()
}
- private fun processConceptCardLiveData(): LiveData {
+ private fun processConceptCardLiveData(): LiveData {
return Transformations.map(conceptCardResultLiveData, ::processConceptCardResult)
}
- private fun processConceptCardResult(conceptCardResult: AsyncResult): ConceptCard {
+ private fun processConceptCardResult(
+ conceptCardResult: AsyncResult
+ ): EphemeralConceptCard {
if (conceptCardResult.isFailure()) {
oppiaLogger.e(
"ConceptCardFragment",
@@ -42,6 +48,6 @@ class ConceptCardViewModel @Inject constructor(
conceptCardResult.getErrorOrNull()!!
)
}
- return conceptCardResult.getOrDefault(ConceptCard.getDefaultInstance())
+ return conceptCardResult.getOrDefault(EphemeralConceptCard.getDefaultInstance())
}
}
diff --git a/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt
index a8090070d35..2e437007ed7 100644
--- a/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/lessons/TopicLessonsFragmentPresenter.kt
@@ -234,7 +234,9 @@ class TopicLessonsFragmentPresenter @Inject constructor(
if (chapterPlayState == ChapterPlayState.IN_PROGRESS_SAVED) {
val explorationCheckpointLiveData =
explorationCheckpointController.retrieveExplorationCheckpoint(
- ProfileId.getDefaultInstance(),
+ ProfileId.newBuilder().apply {
+ internalId = internalProfileId
+ }.build(),
explorationId
).toLiveData()
explorationCheckpointLiveData.observe(
diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/HintsAndSolutionQuestionManagerFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/HintsAndSolutionQuestionManagerFragmentPresenter.kt
index a11f1809887..c6432134ffe 100644
--- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/HintsAndSolutionQuestionManagerFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/HintsAndSolutionQuestionManagerFragmentPresenter.kt
@@ -56,7 +56,8 @@ class HintsAndSolutionQuestionManagerFragmentPresenter @Inject constructor(
// Check if hints are available for this state.
if (ephemeralQuestionState.ephemeralState.state.interaction.hintList.size != 0) {
(activity as HintsAndSolutionQuestionManagerListener).onQuestionStateLoaded(
- ephemeralQuestionState.ephemeralState.state
+ ephemeralQuestionState.ephemeralState.state,
+ ephemeralQuestionState.ephemeralState.writtenTranslationContext
)
}
}
diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/HintsAndSolutionQuestionManagerListener.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/HintsAndSolutionQuestionManagerListener.kt
index fd377ba1635..a08c3cf6d8f 100644
--- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/HintsAndSolutionQuestionManagerListener.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/HintsAndSolutionQuestionManagerListener.kt
@@ -1,9 +1,10 @@
package org.oppia.android.app.topic.questionplayer
import org.oppia.android.app.model.State
+import org.oppia.android.app.model.WrittenTranslationContext
/** Listener for fetching current Question state data. */
interface HintsAndSolutionQuestionManagerListener {
- fun onQuestionStateLoaded(state: State)
+ fun onQuestionStateLoaded(state: State, writtenTranslationContext: WrittenTranslationContext)
}
diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt
index bdd4988c03e..d4153be4460 100644
--- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivity.kt
@@ -10,7 +10,9 @@ import org.oppia.android.app.hintsandsolution.HintsAndSolutionListener
import org.oppia.android.app.hintsandsolution.RevealHintListener
import org.oppia.android.app.hintsandsolution.RevealSolutionInterface
import org.oppia.android.app.model.HelpIndex
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.model.State
+import org.oppia.android.app.model.WrittenTranslationContext
import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_DIALOG
import org.oppia.android.app.player.state.listener.RouteToHintsAndSolutionListener
import org.oppia.android.app.player.state.listener.StateKeyboardButtonListener
@@ -18,8 +20,12 @@ import org.oppia.android.app.player.stopplaying.RestartPlayingSessionListener
import org.oppia.android.app.player.stopplaying.StopExplorationDialogFragment
import org.oppia.android.app.player.stopplaying.StopStatePlayingSessionListener
import org.oppia.android.app.topic.conceptcard.ConceptCardListener
+import org.oppia.android.util.extensions.getProtoExtra
+import org.oppia.android.util.extensions.putProtoExtra
import javax.inject.Inject
+private const val QUESTION_PLAYER_ACTIVITY_PROFILE_ID_ARGUMENT_KEY =
+ "QuestionPlayerActivity.profile_id"
const val QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY =
"QuestionPlayerActivity.skill_id_list"
private const val TAG_STOP_TRAINING_SESSION_DIALOG = "STOP_TRAINING_SESSION_DIALOG"
@@ -39,12 +45,19 @@ class QuestionPlayerActivity :
@Inject
lateinit var questionPlayerActivityPresenter: QuestionPlayerActivityPresenter
+
private lateinit var state: State
+ private lateinit var writtenTranslationContext: WrittenTranslationContext
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(activityComponent as ActivityComponentImpl).inject(this)
- questionPlayerActivityPresenter.handleOnCreate()
+ checkNotNull(intent.extras) { "Expected extras to be defined for QuestionPlayerActivity" }
+ val profileId =
+ intent.getProtoExtra(
+ QUESTION_PLAYER_ACTIVITY_PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance()
+ )
+ questionPlayerActivityPresenter.handleOnCreate(profileId)
}
override fun onBackPressed() {
@@ -68,18 +81,19 @@ class QuestionPlayerActivity :
}
companion object {
- /** Returns a new [Intent] to route to [QuestionPlayerActivity] for a specified skill ID list. */
+ /**
+ * Returns a new [Intent] to route to [QuestionPlayerActivity] for a specified skill ID list and
+ * profile.
+ */
fun createQuestionPlayerActivityIntent(
context: Context,
- skillIdList: ArrayList
+ skillIdList: ArrayList,
+ profileId: ProfileId
): Intent {
- val intent = Intent(context, QuestionPlayerActivity::class.java)
- intent.putExtra(QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY, skillIdList)
- return intent
- }
-
- fun getIntentKey(): String {
- return QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY
+ return Intent(context, QuestionPlayerActivity::class.java).apply {
+ putProtoExtra(QUESTION_PLAYER_ACTIVITY_PROFILE_ID_ARGUMENT_KEY, profileId)
+ putExtra(QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY, skillIdList)
+ }
}
}
@@ -106,7 +120,8 @@ class QuestionPlayerActivity :
HintsAndSolutionDialogFragment.newInstance(
questionId,
state,
- helpIndex
+ helpIndex,
+ writtenTranslationContext
)
hintsAndSolutionDialogFragment.showNow(supportFragmentManager, TAG_HINTS_AND_SOLUTION_DIALOG)
}
@@ -116,8 +131,12 @@ class QuestionPlayerActivity :
getHintsAndSolution()?.dismiss()
}
- override fun onQuestionStateLoaded(state: State) {
+ override fun onQuestionStateLoaded(
+ state: State,
+ writtenTranslationContext: WrittenTranslationContext
+ ) {
this.state = state
+ this.writtenTranslationContext = writtenTranslationContext
}
override fun dismissConceptCard() {
diff --git a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt
index 48b91f7b732..4506674b907 100644
--- a/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityPresenter.kt
@@ -3,13 +3,13 @@ package org.oppia.android.app.topic.questionplayer
import android.view.inputmethod.EditorInfo
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
-import androidx.lifecycle.Observer
import org.oppia.android.R
import org.oppia.android.app.activity.ActivityScope
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.databinding.QuestionPlayerActivityBinding
import org.oppia.android.domain.oppialogger.OppiaLogger
import org.oppia.android.domain.question.QuestionTrainingController
-import org.oppia.android.util.data.AsyncResult
+import org.oppia.android.util.data.DataProviders.Companion.toLiveData
import javax.inject.Inject
const val TAG_QUESTION_PLAYER_FRAGMENT = "TAG_QUESTION_PLAYER_FRAGMENT"
@@ -22,7 +22,11 @@ class QuestionPlayerActivityPresenter @Inject constructor(
private val questionTrainingController: QuestionTrainingController,
private val oppiaLogger: OppiaLogger
) {
- fun handleOnCreate() {
+ private lateinit var profileId: ProfileId
+
+ fun handleOnCreate(profileId: ProfileId) {
+ this.profileId = profileId
+
val binding = DataBindingUtil.setContentView(
activity,
R.layout.question_player_activity
@@ -42,7 +46,7 @@ class QuestionPlayerActivityPresenter @Inject constructor(
startTrainingSessionWithCallback {
activity.supportFragmentManager.beginTransaction().add(
R.id.question_player_fragment_placeholder,
- QuestionPlayerFragment(),
+ QuestionPlayerFragment.newInstance(profileId),
TAG_QUESTION_PLAYER_FRAGMENT
).commitNow()
}
@@ -77,7 +81,7 @@ class QuestionPlayerActivityPresenter @Inject constructor(
// Re-add the player fragment when the new session is ready.
activity.supportFragmentManager.beginTransaction().add(
R.id.question_player_fragment_placeholder,
- QuestionPlayerFragment(),
+ QuestionPlayerFragment.newInstance(profileId),
TAG_QUESTION_PLAYER_FRAGMENT
).commitNow()
}
@@ -87,9 +91,11 @@ class QuestionPlayerActivityPresenter @Inject constructor(
private fun startTrainingSessionWithCallback(callback: () -> Unit) {
val skillIds =
activity.intent.getStringArrayListExtra(QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY)
- questionTrainingController.startQuestionTrainingSession(skillIds).observe(
+ val startDataProvider =
+ questionTrainingController.startQuestionTrainingSession(profileId, skillIds)
+ startDataProvider.toLiveData().observe(
activity,
- Observer {
+ {
when {
it.isPending() -> oppiaLogger.d(
"QuestionPlayerActivity",
@@ -113,9 +119,9 @@ class QuestionPlayerActivityPresenter @Inject constructor(
}
private fun stopTrainingSessionWithCallback(callback: () -> Unit) {
- questionTrainingController.stopQuestionTrainingSession().observe(
+ questionTrainingController.stopQuestionTrainingSession().toLiveData().observe(
activity,
- Observer> {
+ {
when {
it.isPending() -> oppiaLogger.d(
"QuestionPlayerActivity",
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 9807ab485d8..f92e0884e3d 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
@@ -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.ProfileId
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
@@ -18,6 +19,8 @@ import org.oppia.android.app.player.state.listener.ReplayButtonListener
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.putProto
import javax.inject.Inject
/** Fragment that contains all questions in Question Player. */
@@ -46,7 +49,11 @@ class QuestionPlayerFragment :
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
- return questionPlayerFragmentPresenter.handleCreateView(inflater, container)
+ val args = checkNotNull(arguments) {
+ "Expected arguments to be passed to QuestionPlayerFragment"
+ }
+ val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance())
+ return questionPlayerFragmentPresenter.handleCreateView(inflater, container, profileId)
}
override fun onAnswerReadyForSubmission(answer: UserAnswer) {
@@ -87,4 +94,22 @@ class QuestionPlayerFragment :
}
fun dismissConceptCard() = questionPlayerFragmentPresenter.dismissConceptCard()
+
+ companion object {
+ private const val PROFILE_ID_ARGUMENT_KEY = "QuestionPlayerFragment.profile_id"
+
+ /**
+ * Creates a new fragment to play a question session.
+ *
+ * @param profileId the profile in which the question play session will be played
+ * @return a new [QuestionPlayerFragment] to start a question play session
+ */
+ fun newInstance(profileId: ProfileId): QuestionPlayerFragment {
+ return QuestionPlayerFragment().apply {
+ arguments = Bundle().apply {
+ putProto(PROFILE_ID_ARGUMENT_KEY, profileId)
+ }
+ }
+ }
+ }
}
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 e2d908574d4..81aaf6894ab 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.EventLog
import org.oppia.android.app.model.HelpIndex
+import org.oppia.android.app.model.ProfileId
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
@@ -67,7 +68,11 @@ class QuestionPlayerFragmentPresenter @Inject constructor(
private lateinit var currentQuestionState: State
private lateinit var helpIndex: HelpIndex
- fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
+ fun handleCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ profileId: ProfileId
+ ): View? {
binding = QuestionPlayerFragmentBinding.inflate(
inflater,
container,
@@ -75,7 +80,7 @@ class QuestionPlayerFragmentPresenter @Inject constructor(
)
recyclerViewAssembler = createRecyclerViewAssembler(
- assemblerBuilderFactory.create(resourceBucketName, "skill"),
+ assemblerBuilderFactory.create(resourceBucketName, "skill", profileId),
binding.congratulationsTextView,
binding.congratulationsTextConfettiView
)
diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt
index 711589ffa81..f67bf0289fd 100644
--- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityPresenter.kt
@@ -11,12 +11,14 @@ import androidx.lifecycle.Transformations
import org.oppia.android.R
import org.oppia.android.app.activity.ActivityScope
import org.oppia.android.app.help.HelpActivity
-import org.oppia.android.app.model.RevisionCard
+import org.oppia.android.app.model.EphemeralRevisionCard
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.options.OptionsActivity
import org.oppia.android.databinding.RevisionCardActivityBinding
import org.oppia.android.domain.oppialogger.OppiaLogger
import org.oppia.android.domain.topic.TopicController
import org.oppia.android.util.data.AsyncResult
+import org.oppia.android.util.data.DataProviders.Companion.toLiveData
import javax.inject.Inject
/** The presenter for [RevisionCardActivity]. */
@@ -30,7 +32,7 @@ class RevisionCardActivityPresenter @Inject constructor(
private lateinit var revisionCardToolbar: Toolbar
private lateinit var revisionCardToolbarTitle: TextView
- private var internalProfileId = 0
+ private lateinit var profileId: ProfileId
private lateinit var topicId: String
private var subtopicId: Int = 0
@@ -39,7 +41,7 @@ class RevisionCardActivityPresenter @Inject constructor(
activity,
R.layout.revision_card_activity
)
- this.internalProfileId = internalProfileId
+ profileId = ProfileId.newBuilder().setInternalId(internalProfileId).build()
this.topicId = topicId
this.subtopicId = subtopicId
@@ -63,7 +65,7 @@ class RevisionCardActivityPresenter @Inject constructor(
if (getReviewCardFragment() == null) {
activity.supportFragmentManager.beginTransaction().add(
R.id.revision_card_fragment_placeholder,
- RevisionCardFragment.newInstance(topicId, subtopicId)
+ RevisionCardFragment.newInstance(topicId, subtopicId, profileId)
).commitNow()
}
}
@@ -73,17 +75,14 @@ class RevisionCardActivityPresenter @Inject constructor(
return when (item?.itemId) {
R.id.action_preferences -> {
val intent = OptionsActivity.createOptionsActivity(
- activity,
- internalProfileId,
- /* isFromNavigationDrawer= */ false
+ activity, profileId.internalId, isFromNavigationDrawer = false
)
activity.startActivity(intent)
true
}
R.id.action_help -> {
val intent = HelpActivity.createHelpActivityIntent(
- activity, internalProfileId,
- /* isFromNavigationDrawer= */false
+ activity, profileId.internalId, isFromNavigationDrawer = false
)
activity.startActivity(intent)
true
@@ -104,12 +103,12 @@ class RevisionCardActivityPresenter @Inject constructor(
)
}
- val subtopicLiveData: LiveData by lazy {
+ private val subtopicLiveData: LiveData by lazy {
processSubtopicTitleLiveData()
}
- private val revisionCardResultLiveData: LiveData> by lazy {
- topicController.getRevisionCard(topicId, subtopicId)
+ private val revisionCardResultLiveData: LiveData> by lazy {
+ topicController.getRevisionCard(profileId, topicId, subtopicId).toLiveData()
}
private fun processSubtopicTitleLiveData(): LiveData {
@@ -117,7 +116,7 @@ class RevisionCardActivityPresenter @Inject constructor(
}
private fun processSubtopicTitleResult(
- revisionCardResult: AsyncResult
+ revisionCardResult: AsyncResult
): String {
if (revisionCardResult.isFailure()) {
oppiaLogger.e(
@@ -126,10 +125,9 @@ class RevisionCardActivityPresenter @Inject constructor(
revisionCardResult.getErrorOrNull()!!
)
}
- val revisionCard = revisionCardResult.getOrDefault(
- RevisionCard.getDefaultInstance()
- )
- return revisionCard.subtopicTitle
+ val ephemeralRevisionCard =
+ revisionCardResult.getOrDefault(EphemeralRevisionCard.getDefaultInstance())
+ return ephemeralRevisionCard.revisionCard.subtopicTitle
}
private fun getReviewCardFragment(): RevisionCardFragment? {
diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt
index bfd2b8d937c..1f663756944 100755
--- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragment.kt
@@ -7,23 +7,31 @@ import android.view.View
import android.view.ViewGroup
import org.oppia.android.app.fragment.FragmentComponentImpl
import org.oppia.android.app.fragment.InjectableDialogFragment
+import org.oppia.android.app.model.ProfileId
+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 displays revision card */
class RevisionCardFragment : InjectableDialogFragment() {
companion object {
- internal const val TOPIC_ID_ARGUMENT_KEY = "TOPIC_ID_ARGUMENT_KEY"
- internal const val SUBTOPIC_ID_ARGUMENT_KEY = "SUBOPIC_ID_ARGUMENT_KEY"
+ private const val TOPIC_ID_ARGUMENT_KEY = "RevisionCardFragment.topic_id"
+ private const val SUBTOPIC_ID_ARGUMENT_KEY = "RevisionCardFragment.subtopic_id"
+ private const val PROFILE_ID_ARGUMENT_KEY = "RevisionCardFragment.profile_id"
- /** Returns a new [RevisionCardFragment] to display the subtopic content.. */
- fun newInstance(topicId: String, subtopicId: Int): RevisionCardFragment {
- val revisionCardFragment = RevisionCardFragment()
- val args = Bundle()
- args.putString(TOPIC_ID_ARGUMENT_KEY, topicId)
- args.putInt(SUBTOPIC_ID_ARGUMENT_KEY, subtopicId)
- revisionCardFragment.arguments = args
- return revisionCardFragment
+ /**
+ * Returns a new [RevisionCardFragment] to display the specific subtopic for the given topic &
+ * profile.
+ */
+ fun newInstance(topicId: String, subtopicId: Int, profileId: ProfileId): RevisionCardFragment {
+ return RevisionCardFragment().apply {
+ arguments = Bundle().apply {
+ putString(TOPIC_ID_ARGUMENT_KEY, topicId)
+ putInt(SUBTOPIC_ID_ARGUMENT_KEY, subtopicId)
+ putProto(PROFILE_ID_ARGUMENT_KEY, profileId)
+ }
+ }
}
}
@@ -49,7 +57,10 @@ class RevisionCardFragment : InjectableDialogFragment() {
"Expected topicId to be passed to RevisionCardFragment"
}
val subtopicId = args.getInt(SUBTOPIC_ID_ARGUMENT_KEY, -1)
- return revisionCardFragmentPresenter.handleCreateView(inflater, container, topicId, subtopicId)
+ val profileId = args.getProto(PROFILE_ID_ARGUMENT_KEY, ProfileId.getDefaultInstance())
+ return revisionCardFragmentPresenter.handleCreateView(
+ inflater, container, topicId, subtopicId, profileId
+ )
}
/** Dismisses the concept card fragment if it's currently active in this fragment. */
diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt
index fa8bbf83a15..3b9b0e5c97f 100755
--- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentPresenter.kt
@@ -4,14 +4,15 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
-import androidx.lifecycle.Observer
import org.oppia.android.app.fragment.FragmentScope
import org.oppia.android.app.model.EventLog
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.topic.conceptcard.ConceptCardFragment
import org.oppia.android.app.topic.conceptcard.ConceptCardFragment.Companion.CONCEPT_CARD_DIALOG_FRAGMENT_TAG
import org.oppia.android.app.viewmodel.ViewModelProvider
import org.oppia.android.databinding.RevisionCardFragmentBinding
import org.oppia.android.domain.oppialogger.OppiaLogger
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.util.gcsresource.DefaultResourceBucketName
import org.oppia.android.util.parser.html.HtmlParser
import org.oppia.android.util.parser.html.TopicHtmlParserEntityType
@@ -27,15 +28,20 @@ class RevisionCardFragmentPresenter @Inject constructor(
private val htmlParserFactory: HtmlParser.Factory,
@DefaultResourceBucketName private val resourceBucketName: String,
@TopicHtmlParserEntityType private val entityType: String,
- private val viewModelProvider: ViewModelProvider
+ private val viewModelProvider: ViewModelProvider,
+ private val translationController: TranslationController
) : HtmlParser.CustomOppiaTagActionListener {
+ private lateinit var profileId: ProfileId
fun handleCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
topicId: String,
- subtopicId: Int
+ subtopicId: Int,
+ profileId: ProfileId
): View? {
+ this.profileId = profileId
+
val binding =
RevisionCardFragmentBinding.inflate(
inflater,
@@ -45,7 +51,7 @@ class RevisionCardFragmentPresenter @Inject constructor(
val view = binding.revisionCardExplanationText
val viewModel = getReviewCardViewModel()
- viewModel.setTopicAndSubtopicId(topicId, subtopicId)
+ viewModel.initialize(topicId, subtopicId, profileId)
logRevisionCardEvent(topicId, subtopicId)
binding.let {
@@ -55,12 +61,17 @@ class RevisionCardFragmentPresenter @Inject constructor(
viewModel.revisionCardLiveData.observe(
fragment,
- Observer {
+ { ephemeralRevisionCard ->
+ val pageContentsHtml =
+ translationController.extractString(
+ ephemeralRevisionCard.revisionCard.pageContents,
+ ephemeralRevisionCard.writtenTranslationContext
+ )
view.text = htmlParserFactory.create(
resourceBucketName, entityType, topicId, imageCenterAlign = true,
customOppiaTagActionListener = this
).parseOppiaHtml(
- it.pageContents.html, view, supportsLinks = true, supportsConceptCards = true
+ pageContentsHtml, view, supportsLinks = true, supportsConceptCards = true
)
}
)
@@ -91,7 +102,7 @@ class RevisionCardFragmentPresenter @Inject constructor(
override fun onConceptCardLinkClicked(view: View, skillId: String) {
ConceptCardFragment
- .newInstance(skillId)
+ .newInstance(skillId, profileId)
.showNow(fragment.childFragmentManager, CONCEPT_CARD_DIALOG_FRAGMENT_TAG)
}
}
diff --git a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardViewModel.kt b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardViewModel.kt
index bf4e2db00d7..8a2e1fa3cc1 100755
--- a/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardViewModel.kt
+++ b/app/src/main/java/org/oppia/android/app/topic/revisioncard/RevisionCardViewModel.kt
@@ -5,11 +5,13 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import org.oppia.android.app.fragment.FragmentScope
-import org.oppia.android.app.model.RevisionCard
+import org.oppia.android.app.model.EphemeralRevisionCard
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.viewmodel.ObservableViewModel
import org.oppia.android.domain.oppialogger.OppiaLogger
import org.oppia.android.domain.topic.TopicController
import org.oppia.android.util.data.AsyncResult
+import org.oppia.android.util.data.DataProviders.Companion.toLiveData
import javax.inject.Inject
/** [ObservableViewModel] for revision card, providing rich text and worked examples */
@@ -21,11 +23,12 @@ class RevisionCardViewModel @Inject constructor(
) : ObservableViewModel() {
private lateinit var topicId: String
private var subtopicId: Int = 0
+ private lateinit var profileId: ProfileId
private val returnToTopicClickListener: ReturnToTopicClickListener =
activity as ReturnToTopicClickListener
- val revisionCardLiveData: LiveData by lazy {
+ val revisionCardLiveData: LiveData by lazy {
processRevisionCardLiveData()
}
@@ -33,26 +36,24 @@ class RevisionCardViewModel @Inject constructor(
returnToTopicClickListener.onReturnToTopicClicked()
}
- /** Sets the value of topicId, subtopicId before anything else. */
- fun setTopicAndSubtopicId(
- topicId: String,
- subtopicId: Int
- ) {
+ /** Initializes this view model with necessary identifiers. */
+ fun initialize(topicId: String, subtopicId: Int, profileId: ProfileId) {
this.topicId = topicId
this.subtopicId = subtopicId
+ this.profileId = profileId
}
- private val revisionCardResultLiveData: LiveData> by lazy {
- topicController.getRevisionCard(topicId, subtopicId)
+ private val revisionCardResultLiveData: LiveData> by lazy {
+ topicController.getRevisionCard(profileId, topicId, subtopicId).toLiveData()
}
- private fun processRevisionCardLiveData(): LiveData {
+ private fun processRevisionCardLiveData(): LiveData {
return Transformations.map(revisionCardResultLiveData, ::processRevisionCard)
}
private fun processRevisionCard(
- revisionCardResult: AsyncResult
- ): RevisionCard {
+ revisionCardResult: AsyncResult
+ ): EphemeralRevisionCard {
if (revisionCardResult.isFailure()) {
oppiaLogger.e(
"RevisionCardFragment",
@@ -60,9 +61,6 @@ class RevisionCardViewModel @Inject constructor(
revisionCardResult.getErrorOrNull()!!
)
}
-
- return revisionCardResult.getOrDefault(
- RevisionCard.getDefaultInstance()
- )
+ return revisionCardResult.getOrDefault(EphemeralRevisionCard.getDefaultInstance())
}
}
diff --git a/app/src/main/res/layout/concept_card_fragment.xml b/app/src/main/res/layout/concept_card_fragment.xml
index f1af42f0f01..c1a0211df57 100644
--- a/app/src/main/res/layout/concept_card_fragment.xml
+++ b/app/src/main/res/layout/concept_card_fragment.xml
@@ -56,7 +56,7 @@
android:layout_marginTop="@dimen/concept_card_heading_text_margin_top"
android:layout_marginEnd="@dimen/concept_card_heading_text_margin_end"
android:fontFamily="sans-serif-medium"
- android:text="@{viewModel.conceptCardLiveData.skillDescription}"
+ android:text="@{viewModel.conceptCardLiveData.conceptCard.skillDescription}"
android:textColor="@color/oppiaPrimaryText"
android:textSize="20sp" />
diff --git a/app/src/main/res/layout/selection_interaction_item.xml b/app/src/main/res/layout/selection_interaction_item.xml
index 0cafd6a5912..0f81db1b8f5 100644
--- a/app/src/main/res/layout/selection_interaction_item.xml
+++ b/app/src/main/res/layout/selection_interaction_item.xml
@@ -56,6 +56,7 @@
app:allOptionsItemInputType="@{viewModel.getSelectionItemInputType()}"
app:data="@{viewModel.choiceItems}"
app:entityId="@{viewModel.entityId}"
+ app:writtenTranslationContext="@{viewModel.writtenTranslationContext}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
diff --git a/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt
index ee446d6531f..61d2e0d6cc5 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/home/HomeActivityTest.kt
@@ -1469,6 +1469,8 @@ class HomeActivityTest {
)
@RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3840): Make this test work on Espresso & Robolectric.
fun testHomeActivity_initialArabicContext_displaysStringsInArabic() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(EGYPT_ARABIC_LOCALE)
fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_FIXED_FAKE_TIME)
fakeOppiaClock.setCurrentTimeToSameDateTime(MORNING_TIMESTAMP)
launch(createHomeActivityIntent(internalProfileId)).use {
@@ -1492,6 +1494,8 @@ class HomeActivityTest {
)
@RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3840): Make this test work on Espresso & Robolectric.
fun testHomeActivity_initialArabicContext_isInRtlLayout() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(EGYPT_ARABIC_LOCALE)
fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_FIXED_FAKE_TIME)
fakeOppiaClock.setCurrentTimeToSameDateTime(MORNING_TIMESTAMP)
launch(createHomeActivityIntent(internalProfileId)).use {
@@ -1512,6 +1516,8 @@ class HomeActivityTest {
)
@RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
fun testHomeActivity_initialArabicContext_hasArabicDisplayLocale() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(EGYPT_ARABIC_LOCALE)
fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_FIXED_FAKE_TIME)
fakeOppiaClock.setCurrentTimeToSameDateTime(MORNING_TIMESTAMP)
launch(createHomeActivityIntent(internalProfileId)).use {
@@ -1533,6 +1539,8 @@ class HomeActivityTest {
)
@RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3840): Make this test work on Espresso & Robolectric.
fun testHomeActivity_initialBrazilianPortugueseContext_displayStringsInPortuguese() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(BRAZIL_PORTUGUESE_LOCALE)
fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_FIXED_FAKE_TIME)
fakeOppiaClock.setCurrentTimeToSameDateTime(MORNING_TIMESTAMP)
launch(createHomeActivityIntent(internalProfileId)).use {
@@ -1557,6 +1565,8 @@ class HomeActivityTest {
)
@RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3840): Make this test work on Espresso & Robolectric.
fun testHomeActivity_initialBrazilianPortugueseContext_isInLtrLayout() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(BRAZIL_PORTUGUESE_LOCALE)
fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_FIXED_FAKE_TIME)
fakeOppiaClock.setCurrentTimeToSameDateTime(MORNING_TIMESTAMP)
launch(createHomeActivityIntent(internalProfileId)).use {
@@ -1578,6 +1588,8 @@ class HomeActivityTest {
)
@RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
fun testHomeActivity_initialBrazilianPortugueseContext_hasPortugueseDisplayLocale() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(BRAZIL_PORTUGUESE_LOCALE)
fakeOppiaClock.setFakeTimeMode(FakeOppiaClock.FakeTimeMode.MODE_FIXED_FAKE_TIME)
fakeOppiaClock.setCurrentTimeToSameDateTime(MORNING_TIMESTAMP)
launch(createHomeActivityIntent(internalProfileId)).use {
@@ -1631,6 +1643,11 @@ class HomeActivityTest {
)
}
+ private fun forceDefaultLocale(locale: Locale) {
+ context.applicationContext.resources.configuration.setLocale(locale)
+ Locale.setDefault(locale)
+ }
+
private fun verifyTextOnHomeListItemAtPosition(
itemPosition: Int,
targetViewId: Int,
@@ -1747,5 +1764,6 @@ class HomeActivityTest {
private companion object {
private val BRAZIL_PORTUGUESE_LOCALE = Locale("pt", "BR")
+ private val EGYPT_ARABIC_LOCALE = Locale("ar", "EG")
}
}
diff --git a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt
index 522d7890008..181bfa0f4eb 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/player/exploration/ExplorationActivityTest.kt
@@ -19,6 +19,7 @@ import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.contrib.RecyclerViewActions.scrollToHolder
import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.Intents.intended
@@ -41,7 +42,10 @@ import com.google.common.truth.Truth.assertThat
import dagger.Component
import dagger.Module
import dagger.Provides
+import org.hamcrest.BaseMatcher
import org.hamcrest.CoreMatchers.allOf
+import org.hamcrest.CoreMatchers.containsString
+import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers.not
import org.junit.After
@@ -62,8 +66,12 @@ import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.HelpActivity
import org.oppia.android.app.model.ExplorationCheckpoint
+import org.oppia.android.app.model.OppiaLanguage
import org.oppia.android.app.model.ProfileId
+import org.oppia.android.app.model.WrittenTranslationLanguageSelection
import org.oppia.android.app.options.OptionsActivity
+import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel
+import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView
import org.oppia.android.app.shim.ViewBindingShimModule
import org.oppia.android.app.testing.ExplorationInjectionActivity
import org.oppia.android.app.topic.PracticeTabModule
@@ -103,9 +111,15 @@ import org.oppia.android.domain.topic.RATIOS_TOPIC_ID
import org.oppia.android.domain.topic.TEST_EXPLORATION_ID_2
import org.oppia.android.domain.topic.TEST_STORY_ID_0
import org.oppia.android.domain.topic.TEST_TOPIC_ID_0
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
import org.oppia.android.testing.AccessibilityTestRule
+import org.oppia.android.testing.BuildEnvironment
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.TestPlatform
+import org.oppia.android.testing.data.DataProviderTestMonitor
import org.oppia.android.testing.espresso.EditTextInputAction
import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
import org.oppia.android.testing.lightweightcheckpointing.ExplorationCheckpointTestHelper
@@ -152,6 +166,9 @@ class ExplorationActivityTest {
@get:Rule
val accessibilityTestRule = AccessibilityTestRule()
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
+
@Inject
lateinit var explorationCheckpointTestHelper: ExplorationCheckpointTestHelper
@@ -173,6 +190,12 @@ class ExplorationActivityTest {
@Inject
lateinit var fakeOppiaClock: FakeOppiaClock
+ @Inject
+ lateinit var translationController: TranslationController
+
+ @Inject
+ lateinit var monitorFactory: DataProviderTestMonitor.Factory
+
private val internalProfileId: Int = 0
@Before
@@ -516,7 +539,7 @@ class ExplorationActivityTest {
@Test
fun testAudioWithNoConnection_openRatioExploration_clickAudioIcon_checkOpensNoConnectionDialog() {
- setupAudio()
+ setUpAudio()
launch(
createExplorationActivityIntent(
internalProfileId,
@@ -546,7 +569,7 @@ class ExplorationActivityTest {
@Test
fun testAudioWithCellular_openRatioExploration_clickAudioIcon_checkOpensCellularAudioDialog() {
- setupAudio()
+ setUpAudio()
launch(
createExplorationActivityIntent(
internalProfileId, RATIOS_TOPIC_ID,
@@ -574,7 +597,7 @@ class ExplorationActivityTest {
@Test
fun testAudioCellular_ratioExp_audioIcon_configChange_opensCellularAudioDialog() {
- setupAudio()
+ setUpAudio()
launch(
createExplorationActivityIntent(
internalProfileId, RATIOS_TOPIC_ID,
@@ -603,7 +626,7 @@ class ExplorationActivityTest {
@Test
fun testAudioCellular_ratioExp_audioIcon_clickNegative_audioFragmentIsHidden() {
- setupAudio()
+ setUpAudio()
launch(
createExplorationActivityIntent(
internalProfileId, RATIOS_TOPIC_ID,
@@ -641,7 +664,7 @@ class ExplorationActivityTest {
@Test
fun testAudioCellular_ratioExp_audioIcon_clickPositive_checkAudioFragmentIsVisible() {
- setupAudio()
+ setUpAudio()
launch(
createExplorationActivityIntent(
internalProfileId,
@@ -688,7 +711,7 @@ class ExplorationActivityTest {
@Test
fun testAudioCellular_ratioExp_check_negative_audioIcon_audioFragHiddenDialogNotDisplay() {
- setupAudio()
+ setUpAudio()
launch(
createExplorationActivityIntent(
internalProfileId, RATIOS_TOPIC_ID,
@@ -730,7 +753,7 @@ class ExplorationActivityTest {
@Test
fun testAudioCellular_ratioExp_checkPositive_audioIconTwice_audioFragVisDialogNotDisplay() {
- setupAudio()
+ setUpAudio()
launch(
createExplorationActivityIntent(
internalProfileId, RATIOS_TOPIC_ID,
@@ -1583,6 +1606,153 @@ class ExplorationActivityTest {
explorationDataController.stopPlayingExploration()
}
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testExpActivity_englishContentLang_contentIsInEnglish() {
+ updateContentLanguage(
+ ProfileId.newBuilder().apply { internalId = internalProfileId }.build(),
+ OppiaLanguage.ENGLISH
+ )
+ launch(
+ createExplorationActivityIntent(
+ internalProfileId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ TEST_EXPLORATION_ID_2,
+ shouldSavePartialProgress = false
+ )
+ ).use {
+ explorationDataController.startPlayingExploration(
+ internalProfileId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ TEST_EXPLORATION_ID_2,
+ shouldSavePartialProgress = false,
+ explorationCheckpoint = ExplorationCheckpoint.getDefaultInstance()
+ )
+ testCoroutineDispatchers.runCurrent()
+
+ verifyContentContains("Test exploration with interactions")
+ }
+ explorationDataController.stopPlayingExploration()
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testExpActivity_profileWithArabicContentLang_contentIsInArabic() {
+ updateContentLanguage(
+ ProfileId.newBuilder().apply { internalId = internalProfileId }.build(),
+ OppiaLanguage.ARABIC
+ )
+ launch(
+ createExplorationActivityIntent(
+ internalProfileId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ TEST_EXPLORATION_ID_2,
+ shouldSavePartialProgress = false
+ )
+ ).use {
+ explorationDataController.startPlayingExploration(
+ internalProfileId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ TEST_EXPLORATION_ID_2,
+ shouldSavePartialProgress = false,
+ explorationCheckpoint = ExplorationCheckpoint.getDefaultInstance()
+ )
+ testCoroutineDispatchers.runCurrent()
+
+ verifyContentContains("التفاعلات")
+ }
+ explorationDataController.stopPlayingExploration()
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testExpActivity_englishContentLang_showHint_explanationInEnglish() {
+ updateContentLanguage(
+ ProfileId.newBuilder().apply { internalId = internalProfileId }.build(),
+ OppiaLanguage.ENGLISH
+ )
+ launch(
+ createExplorationActivityIntent(
+ internalProfileId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ TEST_EXPLORATION_ID_2,
+ shouldSavePartialProgress = false
+ )
+ ).use {
+ explorationDataController.startPlayingExploration(
+ internalProfileId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ TEST_EXPLORATION_ID_2,
+ shouldSavePartialProgress = false,
+ explorationCheckpoint = ExplorationCheckpoint.getDefaultInstance()
+ )
+ testCoroutineDispatchers.runCurrent()
+ clickContinueButton()
+ // Submit two incorrect answers.
+ submitFractionAnswer(answerText = "1/3")
+ submitFractionAnswer(answerText = "1/4")
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint explanation should be in English.
+ onView(withId(R.id.hints_and_solution_summary))
+ .check(matches(withText(containsString("Remember that two halves"))))
+ }
+ explorationDataController.stopPlayingExploration()
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testExpActivity_profileWithArabicContentLang_showHint_explanationInArabic() {
+ updateContentLanguage(
+ ProfileId.newBuilder().apply { internalId = internalProfileId }.build(),
+ OppiaLanguage.ARABIC
+ )
+ launch(
+ createExplorationActivityIntent(
+ internalProfileId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ TEST_EXPLORATION_ID_2,
+ shouldSavePartialProgress = false
+ )
+ ).use {
+ explorationDataController.startPlayingExploration(
+ internalProfileId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ TEST_EXPLORATION_ID_2,
+ shouldSavePartialProgress = false,
+ explorationCheckpoint = ExplorationCheckpoint.getDefaultInstance()
+ )
+ testCoroutineDispatchers.runCurrent()
+ clickContinueButton()
+ // Submit two incorrect answers.
+ submitFractionAnswer(answerText = "1/3")
+ submitFractionAnswer(answerText = "1/4")
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint explanation should be in Arabic. This helps demonstrate that the activity is
+ // correctly piping the profile ID along to the hint dialog fragment.
+ onView(withId(R.id.hints_and_solution_summary))
+ .check(matches(withText(containsString("واحدة كاملة"))))
+ }
+ explorationDataController.stopPlayingExploration()
+ }
+
private fun createExplorationActivityIntent(
internalProfileId: Int,
topicId: String,
@@ -1601,7 +1771,7 @@ class ExplorationActivityTest {
)
}
- private fun setupAudio() {
+ private fun setUpAudio() {
// Only initialize the Robolectric shadows when running on Robolectric (and use reflection since
// Espresso can't load Robolectric into its classpath).
if (isOnRobolectric()) {
@@ -1662,12 +1832,73 @@ class ExplorationActivityTest {
return onView(isRoot()).perform(waitForMatch(viewMatcher, 30000L))
}
-// 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.
+ private fun updateContentLanguage(profileId: ProfileId, language: OppiaLanguage) {
+ val updateProvider = translationController.updateWrittenTranslationContentLanguage(
+ profileId,
+ WrittenTranslationLanguageSelection.newBuilder().apply {
+ selectedLanguage = language
+ }.build()
+ )
+ monitorFactory.waitForNextSuccessfulResult(updateProvider)
+ }
+
+ private fun clickContinueButton() {
+ scrollToViewType(StateItemViewModel.ViewType.CONTINUE_INTERACTION)
+ onView(withId(R.id.continue_button)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun submitFractionAnswer(answerText: String) {
+ scrollToViewType(StateItemViewModel.ViewType.FRACTION_INPUT_INTERACTION)
+ onView(withId(R.id.fraction_input_interaction_view)).perform(
+ editTextInputAction.appendText(answerText)
+ )
+ testCoroutineDispatchers.runCurrent()
+
+ scrollToViewType(StateItemViewModel.ViewType.SUBMIT_ANSWER_BUTTON)
+ onView(withId(R.id.submit_answer_button)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun openHintsAndSolutionsDialog() {
+ onView(withId(R.id.hints_and_solution_fragment_container)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun pressRevealHintButton(hintPosition: Int) {
+ onView(withId(R.id.hints_and_solution_recycler_view))
+ .inRoot(isDialog())
+ .perform(scrollToPosition(hintPosition * 2))
+ onView(allOf(withId(R.id.reveal_hint_button), isDisplayed()))
+ .inRoot(isDialog())
+ .perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun verifyContentContains(expectedHtml: String) {
+ scrollToViewType(StateItemViewModel.ViewType.CONTENT)
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.state_recycler_view,
+ position = 0,
+ targetViewId = R.id.content_text_view
+ )
+ ).check(matches(withText(containsString(expectedHtml))))
+ }
+
+ private fun scrollToViewType(viewType: StateItemViewModel.ViewType) {
+ onView(withId(R.id.state_recycler_view)).perform(
+ scrollToHolder(StateViewHolderTypeMatcher(viewType))
+ )
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ // 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.
@@ -1705,6 +1936,22 @@ class ExplorationActivityTest {
}
}
+ /**
+ * [BaseMatcher] that matches against the first occurrence of the specified view holder type in
+ * StateFragment's RecyclerView.
+ */
+ private class StateViewHolderTypeMatcher(
+ private val viewType: StateItemViewModel.ViewType
+ ) : BaseMatcher() {
+ override fun describeTo(description: Description?) {
+ description?.appendText("item view type of $viewType")
+ }
+
+ override fun matches(item: Any?): Boolean {
+ return (item as? RecyclerView.ViewHolder)?.itemViewType == viewType.ordinal
+ }
+ }
+
@Module
class TestExplorationStorageModule {
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 a6935627a91..4d3271568af 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
@@ -71,7 +71,9 @@ import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.OppiaLanguage
import org.oppia.android.app.model.ProfileId
+import org.oppia.android.app.model.WrittenTranslationLanguageSelection
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.CONTENT
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.CONTINUE_INTERACTION
@@ -126,12 +128,15 @@ import org.oppia.android.domain.topic.TEST_EXPLORATION_ID_4
import org.oppia.android.domain.topic.TEST_EXPLORATION_ID_5
import org.oppia.android.domain.topic.TEST_STORY_ID_0
import org.oppia.android.domain.topic.TEST_TOPIC_ID_0
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
import org.oppia.android.testing.AccessibilityTestRule
+import org.oppia.android.testing.BuildEnvironment
import org.oppia.android.testing.OppiaTestRule
import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
import org.oppia.android.testing.TestPlatform
+import org.oppia.android.testing.data.DataProviderTestMonitor
import org.oppia.android.testing.environment.TestEnvironmentConfig
import org.oppia.android.testing.espresso.EditTextInputAction
import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
@@ -199,7 +204,13 @@ class StateFragmentTest {
@Inject
lateinit var explorationCheckpointTestHelper: ExplorationCheckpointTestHelper
- private val internalProfileId: Int = 1
+ @Inject
+ lateinit var translationController: TranslationController
+
+ @Inject
+ lateinit var monitorFactory: DataProviderTestMonitor.Factory
+
+ private val profileId = ProfileId.newBuilder().apply { internalId = 1 }.build()
@Before
fun setUp() {
@@ -1491,247 +1502,1053 @@ class StateFragmentTest {
clickReturnToTopicButton()
}
- explorationCheckpointTestHelper
- .verifyExplorationProgressIsDeleted(
- ProfileId.newBuilder().setInternalId(internalProfileId).build(),
- TEST_EXPLORATION_ID_2
- )
- }
-
- private fun addShadowMediaPlayerException(dataSource: Any, exception: Exception) {
- val classLoader = StateFragmentTest::class.java.classLoader!!
- val shadowMediaPlayerClass = classLoader.loadClass("org.robolectric.shadows.ShadowMediaPlayer")
- val addException =
- shadowMediaPlayerClass.getDeclaredMethod(
- "addException", dataSource.javaClass, IOException::class.java
- )
- addException.invoke(/* obj= */ null, dataSource, exception)
+ explorationCheckpointTestHelper.verifyExplorationProgressIsDeleted(
+ profileId, TEST_EXPLORATION_ID_2
+ )
}
- @Suppress("SameParameterValue")
- private fun createAudioDataSource(explorationId: String, audioFileName: String): Any {
- val audioUrl = createAudioUrl(explorationId, audioFileName)
- val classLoader = StateFragmentTest::class.java.classLoader!!
- val dataSourceClass = classLoader.loadClass("org.robolectric.shadows.util.DataSource")
- val toDataSource =
- dataSourceClass.getDeclaredMethod(
- "toDataSource", String::class.java, Map::class.java
- )
- return toDataSource.invoke(/* obj= */ null, audioUrl, /* headers= */ null)
- }
+ // TODO(#503): Add versions of the following multi-language & localization tests for questions.
- private fun createAudioUrl(explorationId: String, audioFileName: String): String {
- return "https://storage.googleapis.com/oppiaserver-resources/" +
- "exploration/$explorationId/assets/audio/$audioFileName"
- }
+ /* Multi-language & localization tests. */
- private fun launchForExploration(
- explorationId: String,
- shouldSavePartialProgress: Boolean
- ): ActivityScenario {
- return launch(
- StateFragmentTestActivity.createTestActivityIntent(
- context,
- internalProfileId,
- TEST_TOPIC_ID_0,
- TEST_STORY_ID_0,
- explorationId,
- shouldSavePartialProgress
- )
- )
- }
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_englishContentLang_content_isInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
- private fun startPlayingExploration() {
- onView(withId(R.id.play_test_exploration_button)).perform(click())
- testCoroutineDispatchers.runCurrent()
+ verifyContentContains("Test exploration with interactions")
+ }
}
- private fun playThroughPrototypeState1() {
- // First state: Continue interaction.
- clickContinueInteractionButton()
- }
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabicContentLang_content_isInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
- private fun playThroughPrototypeState2() {
- // Second state: Fraction input. Correct answer: 1/2.
- typeFractionText("1/2")
- clickSubmitAnswerButton()
- clickContinueNavigationButton()
+ verifyContentContains("التفاعلات")
+ }
}
- private fun playThroughPrototypeState3() {
- // Third state: Multiple choice. Correct answer: Eagle.
- selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle")
- clickContinueNavigationButton()
- }
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabicContentLang_thenEnglish_content_isInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
- private fun playThroughPrototypeState4() {
- // Fourth state: Item selection (radio buttons). Correct answer: Green.
- selectMultipleChoiceOption(optionPosition = 0, expectedOptionText = "Green")
- clickContinueNavigationButton()
+ // The content should be updated to be back in English after the switch.
+ verifyContentContains("Test exploration with interactions")
+ }
}
- private fun playThroughPrototypeState5() {
- // Fifth state: Item selection (checkboxes). Correct answer: {Red, Green, Blue}.
- selectItemSelectionCheckbox(optionPosition = 0, expectedOptionText = "Red")
- selectItemSelectionCheckbox(optionPosition = 2, expectedOptionText = "Green")
- selectItemSelectionCheckbox(optionPosition = 3, expectedOptionText = "Blue")
- clickSubmitAnswerButton()
- clickContinueNavigationButton()
- }
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_continueInteraction_buttonIsInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
- private fun playThroughPrototypeState6() {
- // Sixth state: Numeric input. Correct answer: 121.
- typeNumericInput("121")
- clickSubmitAnswerButton()
- clickContinueNavigationButton()
+ onView(withId(R.id.continue_button)).check(matches(withText("Continue")))
+ }
}
- private fun playThroughPrototypeState7() {
- // Seventh state: Ratio input. Correct answer: 4:5.
- typeRatioExpression("4:5")
- clickSubmitAnswerButton()
- clickContinueNavigationButton()
- }
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_continueInteraction_buttonIsInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
- private fun playThroughPrototypeState8() {
- // Eighth state: Text input. Correct answer: finnish.
- typeTextInput("finnish")
- clickSubmitAnswerButton()
- clickContinueNavigationButton()
+ // App strings aren't being translated, so the button label stays the same.
+ onView(withId(R.id.continue_button)).check(matches(withText("Continue")))
+ }
}
- private fun playThroughPrototypeState9() {
- // Ninth state: Drag Drop Sort. Correct answer: Move 1st item to 4th position.
- dragAndDropItem(fromPosition = 0, toPosition = 3)
- clickSubmitAnswerButton()
- onView(
- atPositionOnView(
- recyclerViewId = R.id.submitted_answer_recycler_view,
- position = 0,
- targetViewId = R.id.submitted_answer_content_text_view
- )
- ).check(matches(withText("3/5")))
- clickContinueNavigationButton()
- }
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_fractionInput_placeholderIsInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
- private fun playThroughPrototypeState10() {
- // Tenth state: Drag Drop Sort with grouping. Correct answer: Merge First Two and after merging
- // move 2nd item to 3rd position.
- mergeDragAndDropItems(position = 1)
- unlinkDragAndDropItems(position = 1)
- mergeDragAndDropItems(position = 0)
- dragAndDropItem(fromPosition = 1, toPosition = 2)
- clickSubmitAnswerButton()
- onView(
- atPositionOnView(
- recyclerViewId = R.id.submitted_answer_recycler_view,
- position = 0,
- targetViewId = R.id.submitted_answer_content_text_view
- )
- ).check(matches(withText("0.6")))
- clickContinueNavigationButton()
- }
+ playThroughPrototypeState1()
- private fun playThroughPrototypeExploration() {
- playThroughPrototypeState1()
- playThroughPrototypeState2()
- playThroughPrototypeState3()
- playThroughPrototypeState4()
- playThroughPrototypeState5()
- playThroughPrototypeState6()
- playThroughPrototypeState7()
- playThroughPrototypeState8()
- playThroughPrototypeState9()
- playThroughPrototypeState10()
+ onView(withId(R.id.fraction_input_interaction_view))
+ .check(matches(withHint("Input a fraction.")))
+ }
}
- private fun rotateToLandscape() {
- onView(isRoot()).perform(orientationLandscape())
- testCoroutineDispatchers.runCurrent()
- }
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_fractionInput_submitAnswer_answerMatchesSubmission() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
- private fun clickContinueInteractionButton() {
- scrollToViewType(CONTINUE_INTERACTION)
- onView(withId(R.id.continue_button)).perform(click())
- testCoroutineDispatchers.runCurrent()
- }
+ typeFractionText("2 1/2")
+ clickSubmitAnswerButton()
- private fun typeFractionText(text: String) {
- scrollToViewType(FRACTION_INPUT_INTERACTION)
- typeTextIntoInteraction(text, interactionViewId = R.id.fraction_input_interaction_view)
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("2 1/2")))
+ }
}
- @Suppress("SameParameterValue")
- private fun typeNumericInput(text: String) {
- scrollToViewType(NUMERIC_INPUT_INTERACTION)
- typeTextIntoInteraction(text, interactionViewId = R.id.numeric_input_interaction_view)
- }
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_fractionInput_placeholderIsInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
- @Suppress("SameParameterValue")
- private fun typeTextInput(text: String) {
- scrollToViewType(TEXT_INPUT_INTERACTION)
- typeTextIntoInteraction(text, interactionViewId = R.id.text_input_interaction_view)
- }
+ playThroughPrototypeState1()
- @Suppress("SameParameterValue")
- private fun typeRatioExpression(text: String) {
- scrollToViewType(RATIO_EXPRESSION_INPUT_INTERACTION)
- typeTextIntoInteraction(text, interactionViewId = R.id.ratio_input_interaction_view)
+ onView(withId(R.id.fraction_input_interaction_view)).check(matches(withHint("إدخال الكسر.")))
+ }
}
- private fun selectMultipleChoiceOption(optionPosition: Int, expectedOptionText: String) {
- clickSelection(
- optionPosition,
- targetClickViewId = R.id.multiple_choice_radio_button,
- expectedText = expectedOptionText,
- targetTextViewId = R.id.multiple_choice_content_text_view
- )
- }
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_fractionInput_submitAnswer_answerMatchesSubmission() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
- private fun selectItemSelectionCheckbox(optionPosition: Int, expectedOptionText: String) {
- clickSelection(
- optionPosition,
- targetClickViewId = R.id.item_selection_checkbox,
- expectedText = expectedOptionText,
- targetTextViewId = R.id.item_selection_contents_text_view
- )
- }
+ typeFractionText("2 1/2")
+ clickSubmitAnswerButton()
- private fun dragAndDropItem(fromPosition: Int, toPosition: Int) {
- scrollToViewType(DRAG_DROP_SORT_INTERACTION)
- onView(withId(R.id.drag_drop_interaction_recycler_view)).perform(
- DragViewAction(
- RecyclerViewCoordinatesProvider(
- fromPosition,
- ChildViewCoordinatesProvider(
- R.id.drag_drop_item_container,
- GeneralLocation.CENTER
- )
- ),
- RecyclerViewCoordinatesProvider(toPosition, CustomGeneralLocation.UNDER_RIGHT),
- Press.FINGER
- )
- )
- testCoroutineDispatchers.runCurrent()
+ // The answer stays the same--the selected language doesn't change how fractions are
+ // represented.
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("2 1/2")))
+ }
}
- private fun mergeDragAndDropItems(position: Int) {
- clickDragAndDropOption(position, targetViewId = R.id.drag_drop_content_group_item)
- }
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_englishContentLang_feedback_isInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
- private fun unlinkDragAndDropItems(position: Int) {
- clickDragAndDropOption(position, targetViewId = R.id.drag_drop_content_unlink_items)
- }
+ typeFractionText("1/2")
+ clickSubmitAnswerButton()
+ scrollToViewType(FEEDBACK)
- @Suppress("SameParameterValue")
- private fun clickImageRegion(pointX: Float, pointY: Float) {
- onView(withId(R.id.image_click_interaction_image_view)).perform(
- clickPoint(pointX, pointY)
- )
- testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.feedback_text_view)).check(matches(withText(containsString("Correct!"))))
+ }
}
- private fun clickSubmitAnswerButton() {
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabicContentLang_feedback_isInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+
+ typeFractionText("1/2")
+ clickSubmitAnswerButton()
+ scrollToViewType(FEEDBACK)
+
+ // The feedback should be in Arabic since the content language is set to that.
+ onView(withId(R.id.feedback_text_view)).check(matches(withText(containsString("صحيح!"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabicContentLang_thenEnglish_feedback_isInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ typeFractionText("1/2")
+ clickSubmitAnswerButton()
+
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ scrollToViewType(FEEDBACK)
+
+ // The feedback should be in Arabic since the content language was just changed.
+ onView(withId(R.id.feedback_text_view)).check(matches(withText(containsString("صحيح!"))))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_multipleChoice_optionsAreInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+
+ playThroughPrototypeState2()
+ scrollToViewType(SELECTION_INTERACTION)
+
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.selection_interaction_recyclerview,
+ position = 2,
+ targetViewId = R.id.multiple_choice_content_text_view
+ )
+ ).check(matches(withText(containsString("Eagle"))))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_multipleChoice_submittedAnswer_answerIsInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+
+ selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle")
+
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("Eagle")))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_multipleChoice_optionsAreInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+
+ playThroughPrototypeState2()
+ scrollToViewType(SELECTION_INTERACTION)
+
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.selection_interaction_recyclerview,
+ position = 2,
+ targetViewId = R.id.multiple_choice_content_text_view
+ )
+ ).check(matches(withText(containsString("النسر"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_multipleChoice_submittedAnswer_answerIsInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+
+ selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "النسر")
+
+ onView(withId(R.id.submitted_answer_text_view))
+ .check(matches(withText(containsString("النسر"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_multipleChoice_submittedAnswer_switchToEnglish_answerIsInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "النسر")
+
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ // The answer should stay in Arabic despite switching back to English.
+ onView(withId(R.id.submitted_answer_text_view))
+ .check(matches(withText(containsString("النسر"))))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_itemSelection_optionsAreInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+
+ scrollToViewType(SELECTION_INTERACTION)
+
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.selection_interaction_recyclerview,
+ position = 0,
+ targetViewId = R.id.item_selection_contents_text_view
+ )
+ ).check(matches(withText(containsString("Red"))))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_itemSelection_submittedAnswer_answerIsInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ scrollToViewType(SELECTION_INTERACTION)
+
+ selectItemSelectionCheckbox(optionPosition = 2, expectedOptionText = "Green")
+ clickSubmitAnswerButton()
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view))
+ .check(matches(withText(containsString("Green"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_itemSelection_optionsAreInArabic() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+
+ scrollToViewType(SELECTION_INTERACTION)
+
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.selection_interaction_recyclerview,
+ position = 0,
+ targetViewId = R.id.item_selection_contents_text_view
+ )
+ ).check(matches(withText(containsString("أحمر"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_itemSelection_submittedAnswer_answerIsInArabic() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ scrollToViewType(SELECTION_INTERACTION)
+
+ selectItemSelectionCheckbox(optionPosition = 2, expectedOptionText = "أخضر")
+ clickSubmitAnswerButton()
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view))
+ .check(matches(withText(containsString("أخضر"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_itemSelection_submittedAnswer_switchToEnglish_answerIsInArabic() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ scrollToViewType(SELECTION_INTERACTION)
+ selectItemSelectionCheckbox(optionPosition = 2, expectedOptionText = "أخضر")
+ clickSubmitAnswerButton()
+
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ // The answer should stay in the language it was submitted in even if the language changes.
+ onView(withId(R.id.submitted_answer_text_view))
+ .check(matches(withText(containsString("أخضر"))))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_numericInput_submitAnswer_answerMatchesSubmission() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ typeNumericInput("121")
+ clickSubmitAnswerButton()
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("121")))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_numericInput_submitAnswer_answerMatchesSubmission() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+
+ typeNumericInput("121")
+ clickSubmitAnswerButton()
+
+ // Arabic doesn't change the display answer for numeric input.
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("121")))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_ratioInput_placeholderIsInEnglish() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ onView(withId(R.id.ratio_input_interaction_view))
+ .check(matches(withHint(containsString("Enter in format of"))))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_ratioInput_submitAnswer_answerMatchesSubmission() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ typeRatioExpression("4:5")
+ clickSubmitAnswerButton()
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("4:5")))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_ratioInput_placeholderIsInArabic() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+
+ onView(withId(R.id.ratio_input_interaction_view))
+ .check(matches(withHint(containsString("بتنسيق"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_ratioInput_submitAnswer_answerMatchesSubmission() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+
+ typeRatioExpression("4:5")
+ clickSubmitAnswerButton()
+
+ // Arabic shouldn't change how ratio answers are displayed.
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("4:5")))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_textInput_placeholderIsInEnglish() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ onView(withId(R.id.text_input_interaction_view))
+ .check(matches(withHint(containsString("Enter a language"))))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_textInput_submitAnswer_answerMatchesSubmission() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ typeTextInput("finnish")
+ clickSubmitAnswerButton()
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("finnish")))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_textInput_placeholderIsInArabic() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+
+ onView(withId(R.id.text_input_interaction_view))
+ .check(matches(withHint(containsString("أدخل لغة"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_textInput_submitAnswer_answerMatchesSubmission() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+
+ typeTextInput("الفنلندية")
+ clickSubmitAnswerButton()
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("الفنلندية")))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_arabic_textInput_submitAnswer_switchToEnglish_answerDoesNotChange() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ typeTextInput("الفنلندية")
+ clickSubmitAnswerButton()
+
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ // Text answers should stay exactly as inputted, even if the content language changes.
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(withId(R.id.submitted_answer_text_view)).check(matches(withText("الفنلندية")))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testStateFragment_english_dragAndDrop_optionsAreInEnglish() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ playThroughPrototypeState8()
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ scrollToViewType(DRAG_DROP_SORT_INTERACTION)
+
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.drag_drop_item_recyclerview,
+ position = 0,
+ targetViewId = R.id.drag_drop_content_text_view
+ )
+ ).check(matches(withText(containsString("0.35"))))
+ }
+ }
+
+ // TODO(#1612): Enable for Robolectric.
+ @Test
+ @RunOn(TestPlatform.ESPRESSO, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_english_dragAndDrop_submittedAnswer_answerIsInEnglish() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ playThroughPrototypeState8()
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ dragAndDropItem(fromPosition = 0, toPosition = 3)
+ clickSubmitAnswerButton()
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.submitted_answer_recycler_view,
+ position = 3,
+ targetViewId = R.id.submitted_answer_content_text_view
+ )
+ ).check(matches(withText("0.35")))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_portuguese_dragAndDrop_optionsAreInPortuguese() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ playThroughPrototypeState8()
+ updateContentLanguage(profileId, OppiaLanguage.BRAZILIAN_PORTUGUESE)
+
+ scrollToViewType(DRAG_DROP_SORT_INTERACTION)
+
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.drag_drop_item_recyclerview,
+ position = 0,
+ targetViewId = R.id.drag_drop_content_text_view
+ )
+ ).check(matches(withText(containsString("0,35"))))
+ }
+ }
+
+ // TODO(#1612): Enable for Robolectric.
+ @Test
+ @RunOn(TestPlatform.ESPRESSO, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_portuguese_dragAndDrop_submittedAnswer_answerIsInPortuguese() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ playThroughPrototypeState8()
+ updateContentLanguage(profileId, OppiaLanguage.BRAZILIAN_PORTUGUESE)
+
+ dragAndDropItem(fromPosition = 0, toPosition = 3)
+ clickSubmitAnswerButton()
+
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.submitted_answer_recycler_view,
+ position = 3,
+ targetViewId = R.id.submitted_answer_content_text_view
+ )
+ ).check(matches(withText("0,35")))
+ }
+ }
+
+ // TODO(#1612): Enable for Robolectric.
+ @Test
+ @RunOn(TestPlatform.ESPRESSO, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_portuguese_dragAndDrop_submittedAnswer_switchToEnglish_answerIsInPt() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = true).use {
+ startPlayingExploration()
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ playThroughPrototypeState8()
+ updateContentLanguage(profileId, OppiaLanguage.BRAZILIAN_PORTUGUESE)
+ dragAndDropItem(fromPosition = 0, toPosition = 3)
+ clickSubmitAnswerButton()
+
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+
+ // The answer should stay in Portuguese even after switching to English.
+ scrollToViewType(SUBMITTED_ANSWER)
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.submitted_answer_recycler_view,
+ position = 3,
+ targetViewId = R.id.submitted_answer_content_text_view
+ )
+ ).check(matches(withText("0,35")))
+ }
+ }
+
+ // TODO(#1612): Enable for Robolectric.
+ @Test
+ @RunOn(TestPlatform.ESPRESSO, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testStateFragment_playWholeLesson_inArabic_hasReturnToTopicButton() {
+ launchForExploration(TEST_EXPLORATION_ID_2, shouldSavePartialProgress = false).use {
+ startPlayingExploration()
+
+ playThroughPrototypeExplorationInArabic()
+
+ // Ninth state: end exploration.
+ scrollToViewType(RETURN_TO_TOPIC_NAVIGATION_BUTTON)
+ onView(withId(R.id.return_to_topic_button)).check(
+ matches(withText(R.string.state_end_exploration_button))
+ )
+ }
+ }
+
+ // TODO(#3171): Implement image region selection tests for English/Arabic to demonstrate that
+ // answers submit normally & with no special behaviors.
+
+ private fun addShadowMediaPlayerException(dataSource: Any, exception: Exception) {
+ val classLoader = StateFragmentTest::class.java.classLoader!!
+ val shadowMediaPlayerClass = classLoader.loadClass("org.robolectric.shadows.ShadowMediaPlayer")
+ val addException =
+ shadowMediaPlayerClass.getDeclaredMethod(
+ "addException", dataSource.javaClass, IOException::class.java
+ )
+ addException.invoke(/* obj= */ null, dataSource, exception)
+ }
+
+ @Suppress("SameParameterValue")
+ private fun createAudioDataSource(explorationId: String, audioFileName: String): Any {
+ val audioUrl = createAudioUrl(explorationId, audioFileName)
+ val classLoader = StateFragmentTest::class.java.classLoader!!
+ val dataSourceClass = classLoader.loadClass("org.robolectric.shadows.util.DataSource")
+ val toDataSource =
+ dataSourceClass.getDeclaredMethod(
+ "toDataSource", String::class.java, Map::class.java
+ )
+ return toDataSource.invoke(/* obj= */ null, audioUrl, /* headers= */ null)
+ }
+
+ private fun createAudioUrl(explorationId: String, audioFileName: String): String {
+ return "https://storage.googleapis.com/oppiaserver-resources/" +
+ "exploration/$explorationId/assets/audio/$audioFileName"
+ }
+
+ private fun launchForExploration(
+ explorationId: String,
+ shouldSavePartialProgress: Boolean
+ ): ActivityScenario {
+ return launch(
+ StateFragmentTestActivity.createTestActivityIntent(
+ context,
+ profileId.internalId,
+ TEST_TOPIC_ID_0,
+ TEST_STORY_ID_0,
+ explorationId,
+ shouldSavePartialProgress
+ )
+ )
+ }
+
+ private fun startPlayingExploration() {
+ onView(withId(R.id.play_test_exploration_button)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun playThroughPrototypeState1() {
+ // First state: Continue interaction.
+ clickContinueInteractionButton()
+ }
+
+ private fun playThroughPrototypeState2() {
+ // Second state: Fraction input. Correct answer: 1/2.
+ typeFractionText("1/2")
+ clickSubmitAnswerButton()
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState3() {
+ // Third state: Multiple choice. Correct answer: Eagle.
+ selectMultipleChoiceOption(optionPosition = 2, expectedOptionText = "Eagle")
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState4() {
+ // Fourth state: Item selection (radio buttons). Correct answer: Green.
+ selectMultipleChoiceOption(optionPosition = 0, expectedOptionText = "Green")
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState5() {
+ // Fifth state: Item selection (checkboxes). Correct answer: {Red, Green, Blue}.
+ selectItemSelectionCheckbox(optionPosition = 0, expectedOptionText = "Red")
+ selectItemSelectionCheckbox(optionPosition = 2, expectedOptionText = "Green")
+ selectItemSelectionCheckbox(optionPosition = 3, expectedOptionText = "Blue")
+ clickSubmitAnswerButton()
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState6() {
+ // Sixth state: Numeric input. Correct answer: 121.
+ typeNumericInput("121")
+ clickSubmitAnswerButton()
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState7() {
+ // Seventh state: Ratio input. Correct answer: 4:5.
+ typeRatioExpression("4:5")
+ clickSubmitAnswerButton()
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState8() {
+ // Eighth state: Text input. Correct answer: finnish.
+ typeTextInput("finnish")
+ clickSubmitAnswerButton()
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState8InArabic() {
+ // Eighth state: Text input. Correct answer: finnish.
+ typeTextInput("الفنلندية")
+ clickSubmitAnswerButton()
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState9() {
+ // Ninth state: Drag Drop Sort. Correct answer: Move 1st item to 4th position.
+ dragAndDropItem(fromPosition = 0, toPosition = 3)
+ clickSubmitAnswerButton()
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.submitted_answer_recycler_view,
+ position = 0,
+ targetViewId = R.id.submitted_answer_content_text_view
+ )
+ ).check(matches(withText("3/5")))
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeState10() {
+ // Tenth state: Drag Drop Sort with grouping. Correct answer: Merge First Two and after merging
+ // move 2nd item to 3rd position.
+ mergeDragAndDropItems(position = 1)
+ unlinkDragAndDropItems(position = 1)
+ mergeDragAndDropItems(position = 0)
+ dragAndDropItem(fromPosition = 1, toPosition = 2)
+ clickSubmitAnswerButton()
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.submitted_answer_recycler_view,
+ position = 0,
+ targetViewId = R.id.submitted_answer_content_text_view
+ )
+ ).check(matches(withText("0.6")))
+ clickContinueNavigationButton()
+ }
+
+ private fun playThroughPrototypeExploration() {
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ playThroughPrototypeState8()
+ playThroughPrototypeState9()
+ playThroughPrototypeState10()
+ }
+
+ private fun playThroughPrototypeExplorationInArabic() {
+ playThroughPrototypeState1()
+ playThroughPrototypeState2()
+ playThroughPrototypeState3()
+ playThroughPrototypeState4()
+ playThroughPrototypeState5()
+ playThroughPrototypeState6()
+ playThroughPrototypeState7()
+ playThroughPrototypeState8InArabic()
+ playThroughPrototypeState9()
+ playThroughPrototypeState10()
+ }
+
+ private fun rotateToLandscape() {
+ onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun clickContinueInteractionButton() {
+ scrollToViewType(CONTINUE_INTERACTION)
+ onView(withId(R.id.continue_button)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun typeFractionText(text: String) {
+ scrollToViewType(FRACTION_INPUT_INTERACTION)
+ typeTextIntoInteraction(text, interactionViewId = R.id.fraction_input_interaction_view)
+ }
+
+ @Suppress("SameParameterValue")
+ private fun typeNumericInput(text: String) {
+ scrollToViewType(NUMERIC_INPUT_INTERACTION)
+ typeTextIntoInteraction(text, interactionViewId = R.id.numeric_input_interaction_view)
+ }
+
+ @Suppress("SameParameterValue")
+ private fun typeTextInput(text: String) {
+ scrollToViewType(TEXT_INPUT_INTERACTION)
+ typeTextIntoInteraction(text, interactionViewId = R.id.text_input_interaction_view)
+ }
+
+ @Suppress("SameParameterValue")
+ private fun typeRatioExpression(text: String) {
+ scrollToViewType(RATIO_EXPRESSION_INPUT_INTERACTION)
+ typeTextIntoInteraction(text, interactionViewId = R.id.ratio_input_interaction_view)
+ }
+
+ private fun selectMultipleChoiceOption(optionPosition: Int, expectedOptionText: String) {
+ clickSelection(
+ optionPosition,
+ targetClickViewId = R.id.multiple_choice_radio_button,
+ expectedText = expectedOptionText,
+ targetTextViewId = R.id.multiple_choice_content_text_view
+ )
+ }
+
+ private fun selectItemSelectionCheckbox(optionPosition: Int, expectedOptionText: String) {
+ clickSelection(
+ optionPosition,
+ targetClickViewId = R.id.item_selection_checkbox,
+ expectedText = expectedOptionText,
+ targetTextViewId = R.id.item_selection_contents_text_view
+ )
+ }
+
+ private fun dragAndDropItem(fromPosition: Int, toPosition: Int) {
+ scrollToViewType(DRAG_DROP_SORT_INTERACTION)
+ onView(withId(R.id.drag_drop_interaction_recycler_view)).perform(
+ DragViewAction(
+ RecyclerViewCoordinatesProvider(
+ fromPosition,
+ ChildViewCoordinatesProvider(
+ R.id.drag_drop_item_container,
+ GeneralLocation.CENTER
+ )
+ ),
+ RecyclerViewCoordinatesProvider(toPosition, CustomGeneralLocation.UNDER_RIGHT),
+ Press.FINGER
+ )
+ )
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun mergeDragAndDropItems(position: Int) {
+ clickDragAndDropOption(position, targetViewId = R.id.drag_drop_content_group_item)
+ }
+
+ private fun unlinkDragAndDropItems(position: Int) {
+ clickDragAndDropOption(position, targetViewId = R.id.drag_drop_content_unlink_items)
+ }
+
+ @Suppress("SameParameterValue")
+ private fun clickImageRegion(pointX: Float, pointY: Float) {
+ onView(withId(R.id.image_click_interaction_image_view)).perform(
+ clickPoint(pointX, pointY)
+ )
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun clickSubmitAnswerButton() {
scrollToViewType(SUBMIT_ANSWER_BUTTON)
onView(withId(R.id.submit_answer_button)).perform(click())
testCoroutineDispatchers.runCurrent()
@@ -1822,6 +2639,16 @@ class StateFragmentTest {
testCoroutineDispatchers.runCurrent()
}
+ private fun updateContentLanguage(profileId: ProfileId, language: OppiaLanguage) {
+ val updateProvider = translationController.updateWrittenTranslationContentLanguage(
+ profileId,
+ WrittenTranslationLanguageSelection.newBuilder().apply {
+ selectedLanguage = language
+ }.build()
+ )
+ monitorFactory.waitForNextSuccessfulResult(updateProvider)
+ }
+
private fun verifyContentContains(expectedHtml: String) {
scrollToViewType(CONTENT)
onView(
diff --git a/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt
index 2ffe1674157..a2ac0544c37 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/recyclerview/RecyclerViewMatcher.kt
@@ -45,9 +45,9 @@ class RecyclerViewMatcher {
public override fun matchesSafely(view: View): Boolean {
this.resources = view.resources
if (childView == null) {
- val recyclerView = view.rootView.findViewById(recyclerViewId) as RecyclerView
- if (recyclerView.id == recyclerViewId) {
- childView = recyclerView.findViewHolderForAdapterPosition(position)!!.itemView
+ val recyclerView = view.rootView.findViewById(recyclerViewId) as? RecyclerView
+ if (recyclerView?.id == recyclerViewId) {
+ childView = recyclerView?.findViewHolderForAdapterPosition(position)?.itemView
} else {
return false
}
@@ -55,8 +55,7 @@ class RecyclerViewMatcher {
return if (targetViewId == -1) {
view === childView
} else {
- val targetView = childView!!.findViewById(targetViewId)
- view === targetView
+ view === childView?.findViewById(targetViewId)
}
}
}
diff --git a/app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt
index 7e93f0e3be1..e3e77cbb345 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/splash/SplashActivityTest.kt
@@ -331,7 +331,7 @@ class SplashActivityTest {
val languageDefinition = displayLocale.localeContext.languageDefinition
assertThat(languageDefinition.language).isEqualTo(LANGUAGE_UNSPECIFIED)
assertThat(languageDefinition.minAndroidSdkVersion).isEqualTo(1)
- assertThat(languageDefinition.appStringId.ietfBcp47Id.ietfLanguageTag).isEqualTo("tr")
+ assertThat(languageDefinition.appStringId.ietfBcp47Id.ietfLanguageTag).isEqualTo("tr-TR")
}
@Test
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/TopicActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/TopicActivityTest.kt
index 3dd267c44a0..549127e6db6 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/TopicActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/TopicActivityTest.kt
@@ -2,13 +2,25 @@ package org.oppia.android.app.topic
import android.app.Application
import android.content.Context
-import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.RecyclerView
+import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition
+import androidx.test.espresso.intent.Intents
+import androidx.test.espresso.intent.Intents.intended
+import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
+import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra
+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 androidx.test.rule.ActivityTestRule
import com.google.common.truth.Truth.assertThat
import dagger.Component
+import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -23,7 +35,9 @@ import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.shim.ViewBindingShimModule
+import org.oppia.android.app.topic.questionplayer.QuestionPlayerActivity
import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule
import org.oppia.android.data.backends.gae.NetworkConfigProdModule
import org.oppia.android.data.backends.gae.NetworkModule
@@ -50,9 +64,12 @@ import org.oppia.android.domain.topic.FRACTIONS_TOPIC_ID
import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule
import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
import org.oppia.android.testing.AccessibilityTestRule
+import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.TestPlatform
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
@@ -86,44 +103,75 @@ class TopicActivityTest {
@get:Rule
val accessibilityTestRule = AccessibilityTestRule()
- @get:Rule
- var activityTestRule: ActivityTestRule = ActivityTestRule(
- TopicActivity::class.java, /* initialTouchMode= */ true, /* launchActivity= */ false
- )
-
@Inject
lateinit var context: Context
- private val internalProfileId = 0
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+
+ private val internalProfileId = 1
@Before
fun setUp() {
+ Intents.init()
setUpTestApplicationComponent()
}
+ @After
+ fun tearDown() {
+ Intents.release()
+ }
+
private fun setUpTestApplicationComponent() {
ApplicationProvider.getApplicationContext().inject(this)
}
@Test
fun testTopicActivity_hasCorrectActivityLabel() {
- activityTestRule.launchActivity(
- createTopicActivityIntent(
- internalProfileId,
- FRACTIONS_TOPIC_ID
- )
- )
- val title = activityTestRule.activity.title
+ launchTopicActivity(internalProfileId, FRACTIONS_TOPIC_ID).use { scenario ->
+ lateinit var title: CharSequence
+ scenario.onActivity { activity -> title = activity.title }
+
+ // Verify that the activity label is correct as a proxy to verify TalkBack will announce the
+ // correct string when it's read out.
+ assertThat(title).isEqualTo(context.getString(R.string.topic_page))
+ }
+ }
- // Verify that the activity label is correct as a proxy to verify TalkBack will announce the
- // correct string when it's read out.
- assertThat(title).isEqualTo(context.getString(R.string.topic_page))
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testTopicActivity_startPracticeSession_questionActivityStartedWithProfileId() {
+ launchTopicActivity(internalProfileId, FRACTIONS_TOPIC_ID).use { scenario ->
+ // Open the practice tab and select a skill.
+ onView(withText("Practice")).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ onView(withText("What is a Fraction?")).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ // Start the practice session.
+ onView(withId(R.id.topic_practice_skill_list))
+ .perform(scrollToPosition(/* position= */ 5))
+ testCoroutineDispatchers.runCurrent()
+ onView(withText("Start")).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ // Verify that the question activity is started with the correct profile ID.
+ val profileId = ProfileId.newBuilder().apply { internalId = internalProfileId }.build()
+ intended(hasComponent(QuestionPlayerActivity::class.java.name))
+ intended(hasExtra("QuestionPlayerActivity.profile_id", profileId.toByteString()))
+ }
}
- private fun createTopicActivityIntent(internalProfileId: Int, topicId: String): Intent {
- return TopicActivity.createTopicActivityIntent(
- ApplicationProvider.getApplicationContext(), internalProfileId, topicId
+ private fun launchTopicActivity(
+ internalProfileId: Int,
+ topicId: String
+ ): ActivityScenario {
+ val scenario = ActivityScenario.launch(
+ TopicActivity.createTopicActivityIntent(context, internalProfileId, topicId)
)
+ testCoroutineDispatchers.runCurrent()
+ onView(withId(R.id.topic_name_text_view)).check(matches(isDisplayed()))
+ return scenario
}
// TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them.
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt
index 7042b66327f..32bc00ac04d 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/conceptcard/ConceptCardFragmentTest.kt
@@ -4,7 +4,7 @@ import android.app.Application
import android.content.Context
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
-import androidx.test.core.app.ActivityScenario.launch
+import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
@@ -21,6 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.Component
import dagger.Module
import dagger.Provides
+import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.instanceOf
import org.hamcrest.Matchers.not
@@ -39,6 +40,9 @@ import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.OppiaLanguage
+import org.oppia.android.app.model.ProfileId
+import org.oppia.android.app.model.WrittenTranslationLanguageSelection
import org.oppia.android.app.shim.ViewBindingShimModule
import org.oppia.android.app.testing.ConceptCardFragmentTestActivity
import org.oppia.android.app.topic.PracticeTabModule
@@ -66,13 +70,20 @@ import org.oppia.android.domain.oppialogger.loguploader.LogUploadWorkerModule
import org.oppia.android.domain.platformparameter.PlatformParameterModule
import org.oppia.android.domain.question.QuestionModule
import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
import org.oppia.android.testing.AccessibilityTestRule
+import org.oppia.android.testing.BuildEnvironment
+import org.oppia.android.testing.OppiaTestRule
import org.oppia.android.testing.RichTextViewMatcher.Companion.containsRichText
+import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.TestPlatform
+import org.oppia.android.testing.data.DataProviderTestMonitor
import org.oppia.android.testing.environment.TestEnvironmentConfig
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
@@ -109,17 +120,33 @@ class ConceptCardFragmentTest {
@get:Rule
val accessibilityTestRule = AccessibilityTestRule()
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
+
@Inject
lateinit var context: Context
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+
+ @Inject
+ lateinit var translationController: TranslationController
+
+ @Inject
+ lateinit var monitorFactory: DataProviderTestMonitor.Factory
+
+ private val profileId = ProfileId.newBuilder().apply { internalId = 1 }.build()
+
@Before
fun setUp() {
Intents.init()
setUpTestApplicationComponent()
+ testCoroutineDispatchers.registerIdlingResource()
}
@After
fun tearDown() {
+ testCoroutineDispatchers.unregisterIdlingResource()
Intents.release()
}
@@ -129,19 +156,25 @@ class ConceptCardFragmentTest {
@Test
fun testConceptCardFragment_clickOnToolbarNavigationButton_closeActivity() {
- launch(ConceptCardFragmentTestActivity::class.java).use {
+ launchTestActivity().use {
onView(withId(R.id.open_dialog_0)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
onView(withContentDescription(R.string.concept_card_close_icon_description))
.inRoot(isDialog())
.perform(click())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.concept_card_toolbar)).check(doesNotExist())
}
}
@Test
fun testConceptCardFragment_toolbarTitle_isDisplayedSuccessfully() {
- launch(ConceptCardFragmentTestActivity::class.java).use {
+ launchTestActivity().use {
onView(withId(R.id.open_dialog_0)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
onView(
allOf(
instanceOf(TextView::class.java),
@@ -153,9 +186,12 @@ class ConceptCardFragmentTest {
@Test
fun testConceptCardFragment_configurationChange_toolbarTitle_isDisplayedSuccessfully() {
- launch(ConceptCardFragmentTestActivity::class.java).use {
+ launchTestActivity().use {
onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.open_dialog_0)).perform(click())
+ testCoroutineDispatchers.runCurrent()
onView(
allOf(
instanceOf(TextView::class.java),
@@ -167,9 +203,13 @@ class ConceptCardFragmentTest {
@Test
fun testConceptCardFragment_configurationChange_conceptCardIsDisplayedCorrectly() {
- launch(ConceptCardFragmentTestActivity::class.java).use {
+ launchTestActivity().use {
onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.open_dialog_0)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.concept_card_explanation_text))
.inRoot(isDialog())
.check(
@@ -187,8 +227,10 @@ class ConceptCardFragmentTest {
@Test
fun testConceptCardFragment_openDialogFragment0_checkSkillAndExplanationAreDisplayedWithoutRichText() { // ktlint-disable max-line-length
- launch(ConceptCardFragmentTestActivity::class.java).use {
+ launchTestActivity().use {
onView(withId(R.id.open_dialog_0)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.concept_card_heading_text))
.inRoot(isDialog())
.check(
@@ -215,8 +257,10 @@ class ConceptCardFragmentTest {
@Test
fun testConceptCardFragment_openDialogFragment1_checkSkillAndExplanationAreDisplayedWithRichText() { // ktlint-disable max-line-length
- launch(ConceptCardFragmentTestActivity::class.java).use {
+ launchTestActivity().use {
onView(withId(R.id.open_dialog_1)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.concept_card_heading_text))
.inRoot(isDialog())
.check(
@@ -243,9 +287,13 @@ class ConceptCardFragmentTest {
@Test
fun testConceptCardFragment_openDialogFragmentWithSkill2_afterConfigurationChange_workedExamplesAreDisplayed() { // ktlint-disable max-line-length
- launch(ConceptCardFragmentTestActivity::class.java).use {
+ launchTestActivity().use {
onView(withId(R.id.open_dialog_1)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.concept_card_heading_text))
.inRoot(isDialog())
.check(
@@ -270,6 +318,72 @@ class ConceptCardFragmentTest {
}
}
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testConceptCardFragment_englishContentLang_explanationIsInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchTestActivity().use {
+ onView(withId(R.id.open_dialog_0)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ onView(withId(R.id.concept_card_explanation_text))
+ .inRoot(isDialog())
+ .check(matches(withText(containsString("Welcome to Oppia"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testConceptCardFragment_englishContentLang_switchToArabic_explanationIsInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchTestActivity().use {
+ onView(withId(R.id.open_dialog_0)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ // Switch to Arabic after opening the card. It should trigger an update to the text with the
+ // correct translation shown.
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+
+ onView(withId(R.id.concept_card_explanation_text))
+ .inRoot(isDialog())
+ .check(matches(withText(containsString("مرحبا بكم في"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testConceptCardFragment_profileWithArabicContentLang_explanationIsInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchTestActivity().use {
+ onView(withId(R.id.open_dialog_0)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+
+ onView(withId(R.id.concept_card_explanation_text))
+ .inRoot(isDialog())
+ .check(matches(withText(containsString("مرحبا بكم في"))))
+ }
+ }
+
+ private fun launchTestActivity(): ActivityScenario {
+ val scenario = ActivityScenario.launch(
+ ConceptCardFragmentTestActivity.createIntent(context, profileId)
+ )
+ testCoroutineDispatchers.runCurrent()
+ return scenario
+ }
+
+ private fun updateContentLanguage(profileId: ProfileId, language: OppiaLanguage) {
+ val updateProvider = translationController.updateWrittenTranslationContentLanguage(
+ profileId,
+ WrittenTranslationLanguageSelection.newBuilder().apply {
+ selectedLanguage = language
+ }.build()
+ )
+ monitorFactory.waitForNextSuccessfulResult(updateProvider)
+ }
+
@Module
class TestModule {
@Provides
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt
index 1624183b405..180997585f8 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/practice/TopicPracticeFragmentTest.kt
@@ -45,6 +45,7 @@ import org.oppia.android.app.topic.EnablePracticeTab
import org.oppia.android.app.topic.PracticeTabModule
import org.oppia.android.app.topic.TopicActivity
import org.oppia.android.app.topic.TopicTab
+import org.oppia.android.app.topic.questionplayer.QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY
import org.oppia.android.app.topic.questionplayer.QuestionPlayerActivity
import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule
import org.oppia.android.app.utility.OrientationChangeAction.Companion.orientationLandscape
@@ -258,7 +259,7 @@ class TopicPracticeFragmentTest {
scrollToPosition(position = 5)
clickPracticeItem(position = 5, targetViewId = R.id.topic_practice_start_button)
intended(hasComponent(QuestionPlayerActivity::class.java.name))
- intended(hasExtra(QuestionPlayerActivity.getIntentKey(), skillIdList))
+ intended(hasExtra(QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY, skillIdList))
}
}
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt
index 6bb1feb15ff..63b6d7d6419 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityTest.kt
@@ -2,7 +2,6 @@ package org.oppia.android.app.topic.questionplayer
import android.app.Application
import android.content.Context
-import android.content.Intent
import android.text.Spannable
import android.text.style.ClickableSpan
import android.view.View
@@ -17,6 +16,7 @@ import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions.scrollToHolder
+import androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
@@ -34,6 +34,7 @@ import dagger.Module
import dagger.Provides
import kotlinx.coroutines.CoroutineDispatcher
import org.hamcrest.BaseMatcher
+import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.Description
import org.hamcrest.Matcher
@@ -53,7 +54,11 @@ import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.OppiaLanguage
+import org.oppia.android.app.model.ProfileId
+import org.oppia.android.app.model.WrittenTranslationLanguageSelection
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel
+import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.CONTENT
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.FEEDBACK
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.SELECTION_INTERACTION
import org.oppia.android.app.recyclerview.RecyclerViewMatcher.Companion.atPositionOnView
@@ -94,12 +99,15 @@ import org.oppia.android.domain.question.WrongAnswerMasteryPenalty
import org.oppia.android.domain.question.WrongAnswerScorePenalty
import org.oppia.android.domain.topic.FRACTIONS_SKILL_ID_0
import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
import org.oppia.android.testing.AccessibilityTestRule
+import org.oppia.android.testing.BuildEnvironment
import org.oppia.android.testing.OppiaTestRule
import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
import org.oppia.android.testing.TestPlatform
+import org.oppia.android.testing.data.DataProviderTestMonitor
import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
import org.oppia.android.testing.profile.ProfileTestHelper
import org.oppia.android.testing.robolectric.RobolectricModule
@@ -164,6 +172,14 @@ class QuestionPlayerActivityTest {
@field:BackgroundDispatcher
lateinit var backgroundCoroutineDispatcher: CoroutineDispatcher
+ @Inject
+ lateinit var translationController: TranslationController
+
+ @Inject
+ lateinit var monitorFactory: DataProviderTestMonitor.Factory
+
+ private val profileId = ProfileId.newBuilder().apply { internalId = 1 }.build()
+
@Before
fun setUp() {
setUpTestApplicationComponent()
@@ -190,19 +206,14 @@ class QuestionPlayerActivityTest {
@Test
fun testQuestionPlayer_hasCorrectActivityLabel() {
- activityTestRule.launchActivity(createQuestionPlayerActivityIntent())
- val title = activityTestRule.activity.title
-
- // Verify that the activity label is correct as a proxy to verify TalkBack will announce the
- // correct string when it's read out.
- assertThat(title).isEqualTo(context.getString(R.string.question_player_activity_title))
- }
+ launchForSkillList(SKILL_ID_LIST).use { scenario ->
+ lateinit var title: CharSequence
+ scenario.onActivity { activity -> title = activity.title }
- private fun createQuestionPlayerActivityIntent(): Intent {
- return QuestionPlayerActivity.createQuestionPlayerActivityIntent(
- context,
- ArrayList(SKILL_ID_LIST)
- )
+ // Verify that the activity label is correct as a proxy to verify TalkBack will announce the
+ // correct string when it's read out.
+ assertThat(title).isEqualTo(context.getString(R.string.question_player_activity_title))
+ }
}
@Test
@@ -350,6 +361,65 @@ class QuestionPlayerActivityTest {
}
}
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testQuestionPlayer_englishContentLang_contentIsInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForSkillList(SKILL_ID_LIST).use {
+ verifyContentContains("picture below represents the fraction")
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testQuestionPlayer_profileWithArabicContentLang_contentIsInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForSkillList(SKILL_ID_LIST).use {
+ verifyContentContains("أدناه يمثل الكسر")
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testQuestionPlayer_englishContentLang_showHint_explanationInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchForSkillList(SKILL_ID_LIST).use {
+ // Submit two incorrect answers.
+ selectMultipleChoiceOption(optionPosition = 3)
+ selectMultipleChoiceOption(optionPosition = 3)
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint explanation should be in English.
+ onView(withId(R.id.hints_and_solution_summary))
+ .check(matches(withText(containsString("number of pieces in the whole"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testQuestionPlayer_profileWithArabicContentLang_showHint_explanationInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForSkillList(SKILL_ID_LIST).use {
+ // Submit two incorrect answers.
+ selectMultipleChoiceOption(optionPosition = 3)
+ selectMultipleChoiceOption(optionPosition = 3)
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint explanation should be in Arabic. This helps demonstrate that the activity is
+ // correctly piping the profile ID along to the hint dialog fragment.
+ onView(withId(R.id.hints_and_solution_summary))
+ .check(matches(withText(containsString("القطع بنفس الحجم"))))
+ }
+ }
+
private fun setUpTestApplicationComponent() {
ApplicationProvider.getApplicationContext().inject(this)
}
@@ -359,7 +429,7 @@ class QuestionPlayerActivityTest {
): ActivityScenario {
val scenario = ActivityScenario.launch(
QuestionPlayerActivity.createQuestionPlayerActivityIntent(
- context, ArrayList(skillIdList)
+ context, ArrayList(skillIdList), profileId
)
)
testCoroutineDispatchers.runCurrent()
@@ -392,6 +462,42 @@ class QuestionPlayerActivityTest {
testCoroutineDispatchers.runCurrent()
}
+ private fun updateContentLanguage(profileId: ProfileId, language: OppiaLanguage) {
+ val updateProvider = translationController.updateWrittenTranslationContentLanguage(
+ profileId,
+ WrittenTranslationLanguageSelection.newBuilder().apply {
+ selectedLanguage = language
+ }.build()
+ )
+ monitorFactory.waitForNextSuccessfulResult(updateProvider)
+ }
+
+ private fun openHintsAndSolutionsDialog() {
+ onView(withId(R.id.hints_and_solution_fragment_container)).perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun pressRevealHintButton(hintPosition: Int) {
+ onView(withId(R.id.hints_and_solution_recycler_view))
+ .inRoot(isDialog())
+ .perform(scrollToPosition(hintPosition * 2))
+ onView(allOf(withId(R.id.reveal_hint_button), isDisplayed()))
+ .inRoot(isDialog())
+ .perform(click())
+ testCoroutineDispatchers.runCurrent()
+ }
+
+ private fun verifyContentContains(expectedHtml: String) {
+ scrollToViewType(CONTENT)
+ onView(
+ atPositionOnView(
+ recyclerViewId = R.id.question_recycler_view,
+ position = 0,
+ targetViewId = R.id.content_text_view
+ )
+ ).check(matches(withText(containsString(expectedHtml))))
+ }
+
private fun scrollToViewType(viewType: StateItemViewModel.ViewType) {
onView(withId(R.id.question_recycler_view)).perform(
scrollToHolder(StateViewHolderTypeMatcher(viewType))
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt
index 9d3e1e5c7ca..88a39e00407 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardActivityTest.kt
@@ -2,13 +2,18 @@ package org.oppia.android.app.topic.revisioncard
import android.app.Application
import android.content.Context
-import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
+import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.rule.ActivityTestRule
import com.google.common.truth.Truth.assertThat
import dagger.Component
+import org.hamcrest.CoreMatchers.containsString
+import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -23,6 +28,9 @@ import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.OppiaLanguage
+import org.oppia.android.app.model.ProfileId
+import org.oppia.android.app.model.WrittenTranslationLanguageSelection
import org.oppia.android.app.shim.ViewBindingShimModule
import org.oppia.android.app.topic.PracticeTabModule
import org.oppia.android.app.translation.testing.ActivityRecreatorTestModule
@@ -49,11 +57,18 @@ import org.oppia.android.domain.platformparameter.PlatformParameterModule
import org.oppia.android.domain.question.QuestionModule
import org.oppia.android.domain.topic.FRACTIONS_TOPIC_ID
import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
import org.oppia.android.testing.AccessibilityTestRule
+import org.oppia.android.testing.BuildEnvironment
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.TestPlatform
+import org.oppia.android.testing.data.DataProviderTestMonitor
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
@@ -88,55 +103,110 @@ class RevisionCardActivityTest {
val accessibilityTestRule = AccessibilityTestRule()
@get:Rule
- val activityTestRule: ActivityTestRule = ActivityTestRule(
- RevisionCardActivity::class.java,
- /* initialTouchMode= */ true,
- /* launchActivity= */ false
- )
+ val oppiaTestRule = OppiaTestRule()
@Inject
lateinit var context: Context
- private val internalProfileId = 0
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
- private val subtopicId = 1
+ @Inject
+ lateinit var translationController: TranslationController
+
+ @Inject
+ lateinit var monitorFactory: DataProviderTestMonitor.Factory
+
+ private val profileId = ProfileId.newBuilder().apply { internalId = 1 }.build()
@Before
fun setUp() {
setUpTestApplicationComponent()
+ testCoroutineDispatchers.registerIdlingResource()
+ }
+
+ @After
+ fun tearDown() {
+ testCoroutineDispatchers.unregisterIdlingResource()
}
@Test
fun testRevisionCardActivity_hasCorrectActivityLabel() {
- activityTestRule.launchActivity(
- createRevisionCardActivityIntent(
- internalProfileId = internalProfileId,
- topicId = FRACTIONS_TOPIC_ID,
- subtopicId = subtopicId
- )
- )
- val title = activityTestRule.activity.title
+ launchRevisionCardActivity(
+ profileId = profileId,
+ topicId = FRACTIONS_TOPIC_ID,
+ subtopicId = 1
+ ).use { scenario ->
+ lateinit var title: CharSequence
+ scenario.onActivity { activity -> title = activity.title }
- // Verify that the activity label is correct as a proxy to verify TalkBack will announce the
- // correct string when it's read out.
- assertThat(title).isEqualTo(context.getString(R.string.revision_card_activity_title))
+ // Verify that the activity label is correct as a proxy to verify TalkBack will announce the
+ // correct string when it's read out.
+ assertThat(title).isEqualTo(context.getString(R.string.revision_card_activity_title))
+ }
+ }
+
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testRevisionCardActivity_englishContentLang_pageContentsAreInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launchRevisionCardActivity(
+ profileId = profileId,
+ topicId = "test_topic_id_0",
+ subtopicId = 1
+ ).use { scenario ->
+ onView(withId(R.id.revision_card_explanation_text))
+ .check(matches(withText(containsString("sample subtopic with dummy content"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testRevisionCardActivity_profileWithArabicContentLang_pageContentsAreInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchRevisionCardActivity(
+ profileId = profileId,
+ topicId = "test_topic_id_0",
+ subtopicId = 1
+ ).use { scenario ->
+ onView(withId(R.id.revision_card_explanation_text))
+ .check(matches(withText(containsString("محاكاة محتوى أكثر واقعية"))))
+ }
}
private fun setUpTestApplicationComponent() {
ApplicationProvider.getApplicationContext().inject(this)
}
+ private fun launchRevisionCardActivity(
+ profileId: ProfileId,
+ topicId: String,
+ subtopicId: Int
+ ): ActivityScenario {
+ val scenario = ActivityScenario.launch(
+ createRevisionCardActivityIntent(profileId.internalId, topicId, subtopicId)
+ )
+ testCoroutineDispatchers.runCurrent()
+ return scenario
+ }
+
private fun createRevisionCardActivityIntent(
internalProfileId: Int,
topicId: String,
subtopicId: Int
- ): Intent {
- return RevisionCardActivity.createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
- topicId,
- subtopicId
+ ) = RevisionCardActivity.createRevisionCardActivityIntent(
+ context, internalProfileId, topicId, subtopicId
+ )
+
+ private fun updateContentLanguage(profileId: ProfileId, language: OppiaLanguage) {
+ val updateProvider = translationController.updateWrittenTranslationContentLanguage(
+ profileId,
+ WrittenTranslationLanguageSelection.newBuilder().apply {
+ selectedLanguage = language
+ }.build()
)
+ monitorFactory.waitForNextSuccessfulResult(updateProvider)
}
// TODO(#59): Figure out a way to reuse modules instead of needing to re-declare them.
diff --git a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt
index 4fcf0da9a5f..9a4fbd12b9e 100644
--- a/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt
+++ b/app/src/sharedTest/java/org/oppia/android/app/topic/revisioncard/RevisionCardFragmentTest.kt
@@ -48,6 +48,9 @@ import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.help.HelpActivity
+import org.oppia.android.app.model.OppiaLanguage
+import org.oppia.android.app.model.ProfileId
+import org.oppia.android.app.model.WrittenTranslationLanguageSelection
import org.oppia.android.app.options.OptionsActivity
import org.oppia.android.app.player.exploration.ExplorationActivity
import org.oppia.android.app.shim.ViewBindingShimModule
@@ -80,12 +83,19 @@ import org.oppia.android.domain.topic.FRACTIONS_TOPIC_ID
import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule
import org.oppia.android.domain.topic.SUBTOPIC_TOPIC_ID
import org.oppia.android.domain.topic.SUBTOPIC_TOPIC_ID_2
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
import org.oppia.android.testing.AccessibilityTestRule
+import org.oppia.android.testing.BuildEnvironment
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.TestPlatform
+import org.oppia.android.testing.data.DataProviderTestMonitor
import org.oppia.android.testing.environment.TestEnvironmentConfig
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
@@ -122,28 +132,51 @@ class RevisionCardFragmentTest {
@get:Rule
val accessibilityTestRule = AccessibilityTestRule()
- private val internalProfileId = 1
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
@Inject
lateinit var context: Context
+ @Inject
+ lateinit var testCoroutineDispatchers: TestCoroutineDispatchers
+
+ @Inject
+ lateinit var translationController: TranslationController
+
+ @Inject
+ lateinit var monitorFactory: DataProviderTestMonitor.Factory
+
+ private val profileId = ProfileId.newBuilder().apply { internalId = 1 }.build()
+
@Before
fun setUp() {
Intents.init()
ApplicationProvider.getApplicationContext().inject(this)
+ testCoroutineDispatchers.registerIdlingResource()
+ }
+
+ @After
+ fun tearDown() {
+ testCoroutineDispatchers.unregisterIdlingResource()
+ Intents.release()
}
@Test
fun testRevisionCardTest_overflowMenu_isDisplayedSuccessfully() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
openActionBarOverflowOrOptionsMenu(context)
+ testCoroutineDispatchers.runCurrent()
+
onView(withText(context.getString(R.string.menu_options))).check(matches(isDisplayed()))
onView(withText(context.getString(R.string.menu_help)))
.check(matches(isDisplayed()))
@@ -154,14 +187,19 @@ class RevisionCardFragmentTest {
fun testRevisionCardTest_openOverflowMenu_selectHelpInOverflowMenu_opensHelpActivity() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID
)
).use {
+ testCoroutineDispatchers.runCurrent()
openActionBarOverflowOrOptionsMenu(context)
+ testCoroutineDispatchers.runCurrent()
+
onView(withText(context.getString(R.string.menu_help))).perform(ViewActions.click())
+ testCoroutineDispatchers.runCurrent()
+
intended(hasComponent(HelpActivity::class.java.name))
intended(
hasExtra(
@@ -176,14 +214,19 @@ class RevisionCardFragmentTest {
fun testRevisionCardTest_openOverflowMenu_selectOptionsInOverflowMenu_opensOptionsActivity() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID
)
).use {
+ testCoroutineDispatchers.runCurrent()
openActionBarOverflowOrOptionsMenu(context)
+ testCoroutineDispatchers.runCurrent()
+
onView(withText(context.getString(R.string.menu_options))).perform(ViewActions.click())
+ testCoroutineDispatchers.runCurrent()
+
intended(hasComponent(OptionsActivity::class.java.name))
intended(
hasExtra(
@@ -198,12 +241,14 @@ class RevisionCardFragmentTest {
fun testRevisionCardTestActivity_toolbarTitle_fractionSubtopicId1_isDisplayedCorrectly() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_toolbar_title))
.check(matches(withText("What is a Fraction?")))
}
@@ -213,12 +258,14 @@ class RevisionCardFragmentTest {
fun testRevisionCardTestActivity_fractionSubtopicId2_checkExplanationAreDisplayedSuccessfully() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID_2
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_explanation_text))
.check(matches(withText(containsString("Description of subtopic is here."))))
}
@@ -228,12 +275,14 @@ class RevisionCardFragmentTest {
fun testRevisionCardTestActivity_fractionSubtopicId1_checkReturnToTopicButtonIsDisplayedSuccessfully() { // ktlint-disable max-line-length
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_return_button))
.check(matches(withText(R.string.return_to_topic)))
}
@@ -243,13 +292,17 @@ class RevisionCardFragmentTest {
fun testRevisionCardTestActivity_configurationChange_toolbarTitle_fractionSubtopicId1_isDisplayedCorrectly() { // ktlint-disable max-line-length
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_toolbar_title))
.check(matches(withText("What is a Fraction?")))
}
@@ -259,13 +312,17 @@ class RevisionCardFragmentTest {
fun testRevisionCardTestActivity_configurationChange_fractionSubtopicId2_checkExplanationAreDisplayedSuccessfully() { // ktlint-disable max-line-length
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID_2
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_explanation_text))
.check(matches(withText(containsString("Description of subtopic is here."))))
}
@@ -275,13 +332,17 @@ class RevisionCardFragmentTest {
fun testRevisionCardTestActivity_configurationChange_fractionSubtopicId1_checkReturnToTopicButtonIsDisplayedSuccessfully() { // ktlint-disable max-line-length
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
SUBTOPIC_TOPIC_ID
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_return_button))
.check(matches(withText(R.string.return_to_topic)))
}
@@ -291,12 +352,14 @@ class RevisionCardFragmentTest {
fun testRevisionCard_showsLinkTextForConceptCard() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
subtopicId = 2
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_explanation_text)).check(
matches(withText(containsString("Learn more")))
)
@@ -307,13 +370,17 @@ class RevisionCardFragmentTest {
fun testRevisionCard_landscape_showsLinkTextForConceptCard() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
subtopicId = 2
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_explanation_text)).check(
matches(withText(containsString("Learn more")))
)
@@ -324,13 +391,17 @@ class RevisionCardFragmentTest {
fun testRevisionCard_clickConceptCardLinkText_opensConceptCard() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
subtopicId = 2
)
).use {
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_explanation_text)).perform(openClickableSpan("Learn more"))
+ testCoroutineDispatchers.runCurrent()
+
onView(withText("Concept Card")).inRoot(isDialog()).check(matches(isDisplayed()))
onView(withId(R.id.concept_card_heading_text))
.inRoot(isDialog())
@@ -342,14 +413,19 @@ class RevisionCardFragmentTest {
fun testRevisionCard_landscape_clickConceptCardLinkText_opensConceptCard() {
launch(
createRevisionCardActivityIntent(
- ApplicationProvider.getApplicationContext(),
- internalProfileId,
+ context,
+ profileId.internalId,
FRACTIONS_TOPIC_ID,
subtopicId = 2
)
).use {
+ testCoroutineDispatchers.runCurrent()
onView(isRoot()).perform(orientationLandscape())
+ testCoroutineDispatchers.runCurrent()
+
onView(withId(R.id.revision_card_explanation_text)).perform(openClickableSpan("Learn more"))
+ testCoroutineDispatchers.runCurrent()
+
onView(withText("Concept Card")).inRoot(isDialog()).check(matches(isDisplayed()))
onView(withId(R.id.concept_card_heading_text))
.inRoot(isDialog())
@@ -357,9 +433,67 @@ class RevisionCardFragmentTest {
}
}
- @After
- fun tearDown() {
- Intents.release()
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC) // TODO(#3858): Enable for Espresso.
+ fun testRevisionCard_englishContentLang_pageContentsAreInEnglish() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launch(
+ createRevisionCardActivityIntent(
+ context,
+ profileId.internalId,
+ "test_topic_id_0",
+ subtopicId = 1
+ )
+ ).use {
+ testCoroutineDispatchers.runCurrent()
+
+ onView(withId(R.id.revision_card_explanation_text))
+ .check(matches(withText(containsString("sample subtopic with dummy content"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testRevisionCard_englishContentLang_switchToArabic_pageContentsAreInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ENGLISH)
+ launch(
+ createRevisionCardActivityIntent(
+ context,
+ profileId.internalId,
+ "test_topic_id_0",
+ subtopicId = 1
+ )
+ ).use {
+ testCoroutineDispatchers.runCurrent()
+
+ // Switch to Arabic after opening the card. It should trigger an update to the text with the
+ // correct translation shown.
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+
+ onView(withId(R.id.revision_card_explanation_text))
+ .check(matches(withText(containsString("محاكاة محتوى أكثر واقعية"))))
+ }
+ }
+
+ // TODO(#3858): Enable for Espresso.
+ @Test
+ @RunOn(TestPlatform.ROBOLECTRIC, buildEnvironments = [BuildEnvironment.BAZEL])
+ fun testRevisionCard_withArabicContentLang_pageContentsAreInArabic() {
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launch(
+ createRevisionCardActivityIntent(
+ context,
+ profileId.internalId,
+ "test_topic_id_0",
+ subtopicId = 1
+ )
+ ).use {
+ testCoroutineDispatchers.runCurrent()
+
+ onView(withId(R.id.revision_card_explanation_text))
+ .check(matches(withText(containsString("محاكاة محتوى أكثر واقعية"))))
+ }
}
/** See the version in StateFragmentTest for documentation details. */
@@ -403,6 +537,16 @@ class RevisionCardFragmentTest {
text: String
): ClickableSpan? = find { text in it.first }?.second
+ private fun updateContentLanguage(profileId: ProfileId, language: OppiaLanguage) {
+ val updateProvider = translationController.updateWrittenTranslationContentLanguage(
+ profileId,
+ WrittenTranslationLanguageSelection.newBuilder().apply {
+ selectedLanguage = language
+ }.build()
+ )
+ monitorFactory.waitForNextSuccessfulResult(updateProvider)
+ }
+
@Module
class TestModule {
@Provides
diff --git a/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt b/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt
index fb52bf555f9..da57d0e7e55 100644
--- a/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/player/state/StateFragmentLocalTest.kt
@@ -39,6 +39,7 @@ import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.hamcrest.Description
import org.hamcrest.Matcher
+import org.hamcrest.Matchers.containsString
import org.hamcrest.TypeSafeMatcher
import org.junit.After
import org.junit.Before
@@ -57,6 +58,11 @@ import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
import org.oppia.android.app.hintsandsolution.TAG_REVEAL_SOLUTION_DIALOG
+import org.oppia.android.app.model.OppiaLanguage
+import org.oppia.android.app.model.OppiaLanguage.ARABIC_VALUE
+import org.oppia.android.app.model.OppiaLanguage.ENGLISH_VALUE
+import org.oppia.android.app.model.ProfileId
+import org.oppia.android.app.model.WrittenTranslationLanguageSelection
import org.oppia.android.app.player.exploration.TAG_HINTS_AND_SOLUTION_DIALOG
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel.ViewType.CONTINUE_INTERACTION
@@ -96,13 +102,20 @@ import org.oppia.android.domain.platformparameter.PlatformParameterModule
import org.oppia.android.domain.question.QuestionModule
import org.oppia.android.domain.topic.FRACTIONS_EXPLORATION_ID_1
import org.oppia.android.domain.topic.PrimeTopicAssetsControllerModule
+import org.oppia.android.domain.topic.TEST_EXPLORATION_ID_2
import org.oppia.android.domain.topic.TEST_STORY_ID_0
import org.oppia.android.domain.topic.TEST_TOPIC_ID_0
+import org.oppia.android.domain.translation.TranslationController
import org.oppia.android.domain.workmanager.WorkManagerConfigurationModule
+import org.oppia.android.testing.BuildEnvironment
+import org.oppia.android.testing.OppiaTestRule
+import org.oppia.android.testing.RunOn
import org.oppia.android.testing.TestLogReportingModule
+import org.oppia.android.testing.data.DataProviderTestMonitor
import org.oppia.android.testing.espresso.EditTextInputAction
import org.oppia.android.testing.espresso.KonfettiViewMatcher.Companion.hasActiveConfetti
import org.oppia.android.testing.espresso.KonfettiViewMatcher.Companion.hasExpectedNumberOfActiveSystems
+import org.oppia.android.testing.junit.DefineAppLanguageLocaleContext
import org.oppia.android.testing.junit.InitializeDefaultLocaleRule
import org.oppia.android.testing.profile.ProfileTestHelper
import org.oppia.android.testing.robolectric.RobolectricModule
@@ -128,6 +141,7 @@ import org.robolectric.annotation.LooperMode
import org.robolectric.shadows.ShadowMediaPlayer
import org.robolectric.shadows.util.DataSource
import java.io.IOException
+import java.util.Locale
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@@ -143,6 +157,9 @@ class StateFragmentLocalTest {
@get:Rule
val initializeDefaultLocaleRule = InitializeDefaultLocaleRule()
+ @get:Rule
+ val oppiaTestRule = OppiaTestRule()
+
private val AUDIO_URL_1 =
createAudioUrl(explorationId = "MjZzEVOG47_1", audioFileName = "content-en-ouqm7j21vt8.mp3")
private val audioDataSource1 = DataSource.toDataSource(AUDIO_URL_1, /* headers= */ null)
@@ -164,7 +181,13 @@ class StateFragmentLocalTest {
@Inject
lateinit var editTextInputAction: EditTextInputAction
- private val internalProfileId: Int = 1
+ @Inject
+ lateinit var translationController: TranslationController
+
+ @Inject
+ lateinit var monitorFactory: DataProviderTestMonitor.Factory
+
+ private val profileId = ProfileId.newBuilder().apply { internalId = 1 }.build()
private val solutionIndex: Int = 4
@Before
@@ -1113,6 +1136,168 @@ class StateFragmentLocalTest {
}
}
+ @Test
+ @DefineAppLanguageLocaleContext(
+ oppiaLanguageEnumId = ENGLISH_VALUE,
+ appStringIetfTag = "en",
+ appStringAndroidLanguageId = ""
+ )
+ fun testStateFragment_englishLocale_defaultContentLang_hint_labelsAreInEnglish() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(Locale.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2).use {
+ startPlayingExploration()
+ clickContinueButton()
+ // Submit two incorrect answers.
+ submitFractionAnswer(answerText = "1/3")
+ submitFractionAnswer(answerText = "1/4")
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint button label should be in English.
+ onView(withId(R.id.reveal_hint_button)).check(matches(withText("Reveal Hint")))
+ }
+ }
+
+ @Test
+ @DefineAppLanguageLocaleContext(
+ oppiaLanguageEnumId = ENGLISH_VALUE,
+ appStringIetfTag = "en",
+ appStringAndroidLanguageId = ""
+ )
+ fun testStateFragment_englishLocale_defaultContentLang_hint_explanationIsInEnglish() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(Locale.ENGLISH)
+ launchForExploration(TEST_EXPLORATION_ID_2).use {
+ startPlayingExploration()
+ clickContinueButton()
+ // Submit two incorrect answers.
+ submitFractionAnswer(answerText = "1/3")
+ submitFractionAnswer(answerText = "1/4")
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint explanation should be in English. Note that an Arabic version of this test doesn't
+ // exist because while the corresponding situation should be true with Arabic, limitations in
+ // the testing framework prevent this from being tested (since activity recreation can't be
+ // done to trigger the
+ onView(withId(R.id.hints_and_solution_summary))
+ .check(matches(withText(containsString("Remember that two halves"))))
+ }
+ }
+
+ @Test
+ @DefineAppLanguageLocaleContext(
+ oppiaLanguageEnumId = ARABIC_VALUE,
+ appStringIetfTag = "ar",
+ appStringAndroidLanguageId = "ar"
+ )
+ fun testStateFragment_arabicLocale_defaultContentLang_hint_labelsAreInArabic() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(EGYPT_ARABIC_LOCALE)
+ launchForExploration(TEST_EXPLORATION_ID_2).use {
+ startPlayingExploration()
+ clickContinueButton()
+ // Submit two incorrect answers.
+ submitFractionAnswer(answerText = "1/3")
+ submitFractionAnswer(answerText = "1/4")
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint button label should be in Arabic.
+ onView(withId(R.id.reveal_hint_button)).check(matches(withText("عرض الملاحظة")))
+ }
+ }
+
+ @Test
+ @DefineAppLanguageLocaleContext(
+ oppiaLanguageEnumId = ARABIC_VALUE,
+ appStringIetfTag = "ar",
+ appStringAndroidLanguageId = "ar"
+ )
+ @RunOn(buildEnvironments = [BuildEnvironment.BAZEL]) // Languages unsupported in Gradle builds.
+ fun testStateFragment_arabicLocale_defaultContentLang_hint_explanationIsInArabic() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(EGYPT_ARABIC_LOCALE)
+ launchForExploration(TEST_EXPLORATION_ID_2).use {
+ startPlayingExploration()
+ clickContinueButton()
+ // Submit two incorrect answers.
+ submitFractionAnswer(answerText = "1/3")
+ submitFractionAnswer(answerText = "1/4")
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint explanation should be in Arabic.
+ onView(withId(R.id.hints_and_solution_summary))
+ .check(matches(withText(containsString("واحدة كاملة"))))
+ }
+ }
+
+ @Test
+ @DefineAppLanguageLocaleContext(
+ oppiaLanguageEnumId = ENGLISH_VALUE,
+ appStringIetfTag = "en",
+ appStringAndroidLanguageId = ""
+ )
+ @RunOn(buildEnvironments = [BuildEnvironment.BAZEL]) // Languages unsupported in Gradle builds.
+ fun testStateFragment_englishLocale_arabicContentLang_hint_labelsAreInEnglish() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(Locale.ENGLISH)
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2).use {
+ startPlayingExploration()
+ clickContinueButton()
+ // Submit two incorrect answers.
+ submitFractionAnswer(answerText = "1/3")
+ submitFractionAnswer(answerText = "1/4")
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint button label should be in English since the app string locale is unaffected by the
+ // content string setting.
+ onView(withId(R.id.reveal_hint_button)).check(matches(withText("Reveal Hint")))
+ }
+ }
+
+ @Test
+ @DefineAppLanguageLocaleContext(
+ oppiaLanguageEnumId = ENGLISH_VALUE,
+ appStringIetfTag = "en",
+ appStringAndroidLanguageId = ""
+ )
+ @RunOn(buildEnvironments = [BuildEnvironment.BAZEL]) // Languages unsupported in Gradle builds.
+ fun testStateFragment_englishLocale_arabicContentLang_hint_explanationIsInArabic() {
+ // Ensure the system locale matches the initial locale context.
+ forceDefaultLocale(Locale.ENGLISH)
+ updateContentLanguage(profileId, OppiaLanguage.ARABIC)
+ launchForExploration(TEST_EXPLORATION_ID_2).use {
+ startPlayingExploration()
+ clickContinueButton()
+ // Submit two incorrect answers.
+ submitFractionAnswer(answerText = "1/3")
+ submitFractionAnswer(answerText = "1/4")
+
+ // Reveal the hint.
+ openHintsAndSolutionsDialog()
+ pressRevealHintButton(hintPosition = 0)
+
+ // The hint explanation should be in Arabic per the content locale override.
+ onView(withId(R.id.hints_and_solution_summary))
+ .check(matches(withText(containsString("واحدة كاملة"))))
+ }
+ }
+
@Test
@Config(qualifiers = "+port")
fun testStateFragment_mobilePortrait_finishExploration_endOfSessionConfettiIsDisplayed() {
@@ -1277,7 +1462,7 @@ class StateFragmentLocalTest {
return ActivityScenario.launch(
StateFragmentTestActivity.createTestActivityIntent(
context,
- internalProfileId,
+ profileId.internalId,
TEST_TOPIC_ID_0,
TEST_STORY_ID_0,
explorationId,
@@ -1619,6 +1804,21 @@ class StateFragmentLocalTest {
testCoroutineDispatchers.runCurrent()
}
+ private fun updateContentLanguage(profileId: ProfileId, language: OppiaLanguage) {
+ val updateProvider = translationController.updateWrittenTranslationContentLanguage(
+ profileId,
+ WrittenTranslationLanguageSelection.newBuilder().apply {
+ selectedLanguage = language
+ }.build()
+ )
+ monitorFactory.waitForNextSuccessfulResult(updateProvider)
+ }
+
+ private fun forceDefaultLocale(locale: Locale) {
+ context.applicationContext.resources.configuration.setLocale(locale)
+ Locale.setDefault(locale)
+ }
+
/**
* Returns a [ViewAssertion] that can be used to check the specified matcher applies the specified
* number of times for children against the view under test. If the count does not exactly match,
@@ -1722,4 +1922,8 @@ class StateFragmentLocalTest {
return (item as? ViewHolder)?.itemViewType == viewType.ordinal
}
}
+
+ private companion object {
+ private val EGYPT_ARABIC_LOCALE = Locale("ar", "EG")
+ }
}
diff --git a/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt b/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt
index d10592365dc..d2146cb3c61 100644
--- a/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt
+++ b/app/src/test/java/org/oppia/android/app/topic/questionplayer/QuestionPlayerActivityLocalTest.kt
@@ -37,6 +37,7 @@ import org.oppia.android.app.application.ApplicationModule
import org.oppia.android.app.application.ApplicationStartupListenerModule
import org.oppia.android.app.devoptions.DeveloperOptionsModule
import org.oppia.android.app.devoptions.DeveloperOptionsStarterModule
+import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.player.state.itemviewmodel.StateItemViewModel
import org.oppia.android.app.shim.ViewBindingShimModule
import org.oppia.android.app.topic.PracticeTabModule
@@ -277,7 +278,7 @@ class QuestionPlayerActivityLocalTest {
): ActivityScenario {
return ActivityScenario.launch(
QuestionPlayerActivity.createQuestionPlayerActivityIntent(
- context, skillIdList
+ context, skillIdList, ProfileId.getDefaultInstance()
)
)
}
diff --git a/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt b/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt
index 9036371751a..67d1c685f6b 100644
--- a/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt
+++ b/app/src/test/java/org/oppia/android/app/translation/AppLanguageWatcherMixinTest.kt
@@ -29,6 +29,7 @@ import org.oppia.android.app.model.AppLanguageSelection
import org.oppia.android.app.model.OppiaLanguage
import org.oppia.android.app.model.OppiaLanguage.BRAZILIAN_PORTUGUESE
import org.oppia.android.app.model.OppiaLanguage.ENGLISH
+import org.oppia.android.app.model.OppiaRegion
import org.oppia.android.app.model.ProfileId
import org.oppia.android.app.shim.ViewBindingShimModule
import org.oppia.android.app.testing.activity.TestActivity
@@ -93,7 +94,10 @@ import javax.inject.Singleton
@DefineAppLanguageLocaleContext(
oppiaLanguageEnumId = OppiaLanguage.ENGLISH_VALUE,
appStringIetfTag = "en",
- appStringAndroidLanguageId = "en"
+ appStringAndroidLanguageId = "en",
+ oppiaRegionEnumId = OppiaRegion.UNITED_STATES_VALUE,
+ regionLanguageEnumIds = [OppiaLanguage.ENGLISH_VALUE],
+ regionIetfTag = "US"
)
class AppLanguageWatcherMixinTest {
// TODO(#1720): Add a test to verify that the mixin does nothing when a language change occurs
diff --git a/build_flavors.bzl b/build_flavors.bzl
index b8f73684959..cfcda2dbead 100644
--- a/build_flavors.bzl
+++ b/build_flavors.bzl
@@ -21,6 +21,7 @@ _FLAVOR_METADATA = {
"target_sdk_version": 29,
"multidex": "native", # Legacy multidex not needed for dev builds.
"proguard_specs": [], # Developer builds are not optimized.
+ "production_release": False,
"deps": [
"//app",
],
@@ -44,6 +45,7 @@ _FLAVOR_METADATA = {
"config/proguard/oppia-prod-proguard-rules.pro",
"config/proguard/protobuf-proguard-rules.pro",
],
+ "production_release": True,
"deps": [
"//app",
],
@@ -140,6 +142,7 @@ def define_oppia_binary_flavor(flavor):
oppia_android_application(
name = "oppia_%s" % flavor,
custom_package = "org.oppia.android",
+ testonly = not _FLAVOR_METADATA[flavor]["production_release"],
enable_data_binding = True,
config_file = "//:bundle_config.pb.json",
manifest = ":AndroidManifest_transformed_%s.xml" % flavor,
diff --git a/config/src/java/org/oppia/android/config/languages/supported_languages.textproto b/config/src/java/org/oppia/android/config/languages/supported_languages.textproto
index 4e6d3f35138..d89e04ebb48 100644
--- a/config/src/java/org/oppia/android/config/languages/supported_languages.textproto
+++ b/config/src/java/org/oppia/android/config/languages/supported_languages.textproto
@@ -28,6 +28,10 @@ language_definitions {
ietf_language_tag: "en"
}
android_resources_language_id {
+ # Note that while English is the default language for Oppia & no special language code is
+ # needed for it, "en" is still used here for consistency. Technically this will match strings
+ # against "values-en", but because the team will never define strings with that qualifier they
+ # will always fallback to the default English strings under "values".
language_code: "en"
}
}
@@ -82,6 +86,11 @@ language_definitions {
language_definitions {
language: PORTUGUESE
min_android_sdk_version: 1
+ content_string_id {
+ ietf_bcp47_id {
+ ietf_language_tag: "pt"
+ }
+ }
}
language_definitions {
language: BRAZILIAN_PORTUGUESE
diff --git a/domain/BUILD.bazel b/domain/BUILD.bazel
index 61dfb4b2a83..9faa5f96aac 100755
--- a/domain/BUILD.bazel
+++ b/domain/BUILD.bazel
@@ -67,6 +67,7 @@ DOMAIN_ASSETS = generate_assets_list_from_text_protos(
"GJ2rLXRKD5hw_3",
"GJ2rLXRKD5hw_4",
"omzF4oqgeTXd_1",
+ "test_topic_id_0_1",
],
topic_file_names = [
"test_topic_id_0",
@@ -105,6 +106,7 @@ kt_android_library(
"//domain/src/main/java/org/oppia/android/domain/state:state_deck",
"//domain/src/main/java/org/oppia/android/domain/state:state_graph",
"//domain/src/main/java/org/oppia/android/domain/state:state_list",
+ "//domain/src/main/java/org/oppia/android/domain/translation:translation_controller",
"//domain/src/main/java/org/oppia/android/domain/util:asset",
"//domain/src/main/java/org/oppia/android/domain/util:extensions",
"//domain/src/main/java/org/oppia/android/domain/util:retriever",
@@ -165,15 +167,19 @@ TEST_DEPS = [
":domain",
":interaction_object_test_builder",
":testing",
+ "//app:crashlytics",
+ "//app:crashlytics_deps",
"//data/src/main/java/org/oppia/android/data/backends/gae:network_config_prod_module",
"//data/src/main/java/org/oppia/android/data/backends/gae/model",
"//data/src/main/java/org/oppia/android/data/persistence:cache_store",
"//domain/src/main/java/org/oppia/android/domain/feedbackreporting:prod_module",
"//domain/src/main/java/org/oppia/android/domain/feedbackreporting:report_schema_version",
+ "//domain/src/main/java/org/oppia/android/domain/oppialogger/exceptions:logger_module",
"//domain/src/main/java/org/oppia/android/domain/oppialogger/exceptions:startup_listener",
"//domain/src/main/java/org/oppia/android/domain/oppialogger/loguploader:worker_factory",
"//domain/src/main/java/org/oppia/android/domain/oppialogger/loguploader:worker_module",
"//testing",
+ "//testing/src/main/java/org/oppia/android/testing/data:data_provider_test_monitor",
"//testing/src/main/java/org/oppia/android/testing/network:network",
"//testing/src/main/java/org/oppia/android/testing/network:test_module",
"//testing/src/main/java/org/oppia/android/testing/platformparameter:test_module",
diff --git a/domain/src/main/assets/13.json b/domain/src/main/assets/13.json
index 8f384ee3b0f..0ff6b0de55c 100644
--- a/domain/src/main/assets/13.json
+++ b/domain/src/main/assets/13.json
@@ -2,7 +2,7 @@
"exploration_id": "13",
"preferred_audio_language_code": "",
"correctness_feedback_enabled": false,
- "version": 1,
+ "version": 0,
"record_playthrough_probability": 0.0,
"exploration": {
"init_state_name": "ImageClickInput",
@@ -27,7 +27,7 @@
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {}
@@ -35,7 +35,18 @@
},
"written_translations": {
"translations_mapping": {
- "content": {}
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Este é o fim e nbsp;
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " هذه هي النهاية & nbsp؛
"},
+ "needs_update": false
+ }
+ }
}
},
"solicit_answer_details": false,
@@ -55,62 +66,62 @@
},
"imageAndRegions": {
"value": {
+ "imagePath": "Screen Shot 2015-02-18 at 3.51.49 AM_height_400_width_816.png",
"labeledRegions": [{
+ "label": "Mercury",
"region": {
"regionType": "Rectangle",
- "area": [[0.010981757685823261, 0.09856375700297611], [0.04871760674242703, 0.17073356832373082]]
- },
- "label": "Mercury"
+ "area": [[0.010981758125126362, 0.09856376051902771], [0.04871760681271553, 0.17073357105255127]]
+ }
}, {
+ "label": "Venus",
"region": {
"regionType": "Rectangle",
- "area": [[0.036925153912238357, 0.1755448890784478], [0.10060439919525722, 0.2958279079463723]]
- },
- "label": "Venus"
+ "area": [[0.03692515566945076, 0.1755448877811432], [0.10060440003871918, 0.2958278954029083]]
+ }
}, {
+ "label": "Earth",
"region": {
"regionType": "Rectangle",
- "area": [[0.08645345579903081, 0.10818639851241008], [0.15484968221412515, 0.22846941738033458]]
- },
- "label": "Earth"
+ "area": [[0.08645345270633698, 0.10818640142679214], [0.15484967827796936, 0.22846941649913788]]
+ }
}, {
+ "label": "Mars",
"region": {
"regionType": "Rectangle",
- "area": [[0.14777421051601194, 0.1755448890784478], [0.19258553127072892, 0.2717713041727874]]
- },
- "label": "Mars"
+ "area": [[0.14777420461177826, 0.1755448877811432], [0.1925855278968811, 0.27177131175994873]]
+ }
}, {
+ "label": "Jupiter",
"region": {
"regionType": "Rectangle",
- "area": [[0.19022704070469118, 0.06007319096524027], [0.5723025124028044, 0.8154505494558062]]
- },
- "label": "Jupiter"
+ "area": [[0.19022704660892487, 0.060073189437389374], [0.572302520275116, 0.8154505491256714]]
+ }
}, {
+ "label": "Saturn",
"region": {
"regionType": "Rectangle",
- "area": [[0.2491893048556346, 0.36799771926712704], [0.9968308142895969, 0.9934694173803345]]
- },
- "label": "Saturn"
+ "area": [[0.24918930232524872, 0.3679977059364319], [0.9968308210372925, 0.9934694170951843]]
+ }
}, {
+ "label": "Uranus",
"region": {
"regionType": "Rectangle",
- "area": [[0.664283644478276, 0.05045054945580631], [0.8152270407046912, 0.3535637570029761]]
- },
- "label": "Uranus"
+ "area": [[0.6642836332321167, 0.05045054852962494], [0.8152270317077637, 0.35356375575065613]]
+ }
}, {
+ "label": "Neptune",
"region": {
"regionType": "Rectangle",
- "area": [[0.8246610029688421, 0.021582624927504426], [0.9402270407046912, 0.2525260211539195]]
- },
- "label": "Neptune"
+ "area": [[0.8246610164642334, 0.021582625806331635], [0.9402270317077637, 0.25252601504325867]]
+ }
}, {
+ "label": "Pluto",
"region": {
"regionType": "Rectangle",
- "area": [[0.9402270407046912, 0.22365809662561761], [0.977962889761295, 0.2958279079463723]]
- },
- "label": "Pluto"
- }],
- "imagePath": "Screen Shot 2015-02-18 at 3.51.49 AM_height_400_width_816.png"
+ "area": [[0.9402270317077637, 0.22365809977054596], [0.9779629111289978, 0.2958278954029083]]
+ }
+ }]
}
}
},
@@ -129,10 +140,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "IsInRegion",
@@ -148,10 +159,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "IsInRegion",
@@ -167,10 +178,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "IsInRegion",
@@ -186,10 +197,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "IsInRegion",
@@ -205,10 +216,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "IsInRegion",
@@ -224,10 +235,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "IsInRegion",
@@ -243,10 +254,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "IsInRegion",
@@ -262,10 +273,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "IsInRegion",
@@ -281,10 +292,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "ImageClickInput",
@@ -294,13 +305,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {},
@@ -318,17 +329,138 @@
},
"written_translations": {
"translations_mapping": {
- "content": {},
- "feedback_2": {},
- "feedback_3": {},
- "feedback_1": {},
- "feedback_6": {},
- "feedback_7": {},
- "feedback_4": {},
- "feedback_5": {},
- "default_outcome": {},
- "feedback_8": {},
- "feedback_9": {}
+ "content": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Nosso sistema solar consiste em nove planetas orbitando em torno de nosso sol: Mercúrio, Venus, Terra, Marte, Júpiter, Saturno, Urano, Netuno e Plutão. Eles variam de gigantes de gás enormes a pequenos planetas anões gelados.
Clique nos planetas abaixo para descobrir mais sobre cada um!
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " يتكون نظامنا الشمسي من تسعة كواكب تدور حول الشمس: الزئبق، فينوس، الأرض، المريخ، كوكب المشتري، زحل، أورانوس، نبتون، بلوتو. وهي تتراوح بين عمالقة الغاز الضخمة إلى كواكب الأقزم القزم الصغيرة.
انقر على الكواكب أدناه لمعرفة المزيد عن كل واحد!
"},
+ "needs_update": false
+ }
+ },
+ "feedback_2": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Este é venus, o segundo planeta do sol. É o planeta mais brilhante do sistema solar, e às vezes pode ser visto durante o dia. Embora o mercúrio esteja mais perto do sol do que Vênus, Venus é o planeta mais quente do sistema solar, porque sua atmosfera espessa de dióxido de carbono traga calor na sua superfície.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " هذا هو فينوس، والكوكب الثاني من الشمس. إنه ألمع كوكب في النظام الشمسي، ويمكن أن ينظر إليه في بعض الأحيان خلال اليوم. على الرغم من أن الزئبق أقرب إلى الشمس من فينوس، فإن فينوس هو سخونة كوكب في النظام الشمسي، لأن جو سميكة ثاني أكسيد الكربون الفخاخ الحرارة على سطحه.
"},
+ "needs_update": false
+ }
+ },
+ "feedback_3": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Terra! Nosso planeta de casa é o terceiro planeta do sol. Apesar da vastidão do sistema solar e do resto do espaço, é o único lugar que sabemos que a vida existe. É apenas a distância certa do sol para garantir uma temperatura confortável para que existam.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " الأرض! كوكبنا الرئيسي هو الكوكب الثالث من الشمس. على الرغم من استمرار النظام الشمسي وبقية المساحة، فإن المكان الوحيد الذي نعرفه أن الحياة موجودة. في المسافة اليمنى فقط من الشمس لضمان درجة حرارة مريحة بالنسبة لنا موجودة فيها.
"},
+ "needs_update": false
+ }
+ },
+ "feedback_1": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Isso é mercúrio, um pequeno planeta metálico mais próximo do sol. Como é tão perto do sol, leva apenas 88 dias para viajar uma órbita inteira, então um ano em Mercúrio só seria de 88 dias.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : "\n Saturno, o sexto planeta do sol, é mais conhecido por seus belos anéis de gelo e poeira em torno dele. Como Júpiter, é um gigante de gás, principalmente feito de camadas de hidrogênio que ficam progressivamente mais densos em relação ao seu núcleo sólido.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " Saturn، الكوكب السادس من الشمس، هو معروف جيدا لحلقاتها الجميلة من الجليد والغبار المدار حولها. مثل كوكب المشتري، فهي عملاقة غازية، معظمها مصنوعة من طبقات الهيدروجين التي تحصل على كثافة تدريجية نحو جوهرها الصلب.
"},
+ "needs_update": false
+ }
+ },
+ "feedback_7": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Urano é o sétimo planeta do sol. Foi o primeiro planeta que foi descoberto usando um telescópio, já que é normalmente muito obscuro para ser visto com os olhos nus. É o planeta mais frio, e é formado de camadas de gás de hidrogênio envolvendo um núcleo frígido de rocha e gelo.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " أورانوس هو الكوكب السابع من الشمس. كان أول كوكب يتم اكتشافه باستخدام التلسكوب ، لأنه عادة ما يكون خافتًا جدًا بحيث لا يمكن رؤيته بالعين المجردة. إنه أبرد كوكب ، ويتكون من طبقات من غاز الهيدروجين تحيط بنواة متجمدة من الصخور والجليد.
"},
+ "needs_update": false
+ }
+ },
+ "feedback_4": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Marte é o quarto planeta do sol. Muitas vezes é apelidado do \"Planeta Vermelho\" devido à sua cor, que é resultado de grandes quantidades de óxido de ferro em sua superfície. Tem uma atmosfera fina de dióxido de carbono e cápsulas de gelo em cada extremidade, e tem duas pequenas luas de forma irregular chamada Phobos e Deimos.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " المريخ هو الكوكب الرابع من الشمس. غالبا ما يطلق عليه \"الكوكب الأحمر\" بسبب لونه، وهو نتيجة كميات كبيرة من أكسيد الحديد على سطحها. يحتوي على جو رقيق من ثاني أكسيد الكربون والقبعات الجليدية في أي من الطرفين، ولديه اثنين من أقمار صغيرة على شكل غير منتظم يسمى فوبوس وديموس.
"},
+ "needs_update": false
+ }
+ },
+ "feedback_5": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Júpiter, o quinto planeta a partir do Sol, é um enorme planeta 2,5 vezes mais pesado do que todos os outros planetas do nosso Sistema Solar combinados. É principalmente formado por gases espessos em torno de um núcleo sólido. Uma característica famosa é a \"Grande Mancha Vermelha\", que é uma enorme tempestade em sua superfície que assola há pelo menos 350 anos e é grande o suficiente para engolir três Terras inteiras.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " كوكب المشتري، الكوكب الخامس من الشمس، هو كوكب ضخم يبلغ من العمر 2.5 مرة من كل الكواكب الأخرى في نظامنا الشمسي مجتمعة. يتم تشكيلها في الغالب من الغازات السميكة حول جوهر صلب. ميزة واحدة مشهورة بها هي \"بقعة حمراء رائعة\"، وهي عاصفة كبيرة على سطحها الذي اندلعت لمدة 350 عاما على الأقل، وهو كبير بما يكفي لابتلاع ثلاثة أذرع بأكملها.
"},
+ "needs_update": false
+ }
+ },
+ "default_outcome": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Isso não parece ser a escolha correta.Tente novamente
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " هذا لا يبدو وكأنه الاختيار الصحيح. مرة أخرى
"},
+ "needs_update": false
+ }
+ },
+ "feedback_8": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Netuno é o oitavo planeta do sol. É um \"gigante de gelo\", como Urano, com uma camada externa de hidrogênio, hélio e metano envolvendo um núcleo gelado. O metano em sua atmosfera absorve a luz vermelha, dando-lhe uma bela cor azul. Foi previsto primeiro existir de cálculos gravitacionais, e só foi descoberto pelo telescópio mais tarde.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " نبتون هو الكوكب الثامن من الشمس. إنه \"عملاق ثلج\"، مثل أورانوس، بطبقة خارجية من الهيدروجين والهيليوم والميثان يغليف جوهر جليدي. يمتص الميثان في جوها الضوء الأحمر، مما يمنحه اللون الأزرق الجميل. تم توقع وجوده لأول مرة من حسابات الجاذبية، وتم اكتشافه فقط بواسطة التلسكوب في وقت لاحق.
"},
+ "needs_update": false
+ }
+ },
+ "feedback_9": {
+ "pt": {
+ "data_format": "html",
+ "translation": {"translation" : " Plutão já foi considerado o nono planeta do sol, mas agora foi reclassificado como um \"planeta anão\". É formado por gelo e rocha, e é ainda menor que a nossa lua. A primeira espaçonave a visitar Plutão é devido ao alcance em julho de 2015.
"},
+ "needs_update": false
+ },
+ "ar": {
+ "data_format": "html",
+ "translation": {"translation" : " كان بلوتو يعتبر مرة واحدة الكوكب التاسع من الشمس، لكن تم إعادة تصنيفه الآن باعتباره \"كوكب قزم\". يتم تشكيله من الجليد والصخور، وهو أصغر من قمرنا. من المقرر أن يصل المركبة الفضائية الأولى إلى زيارة بلوتو في يوليو 2015.
"},
+ "needs_update": false
+ }
+ }
}
},
"solicit_answer_details": false,
diff --git a/domain/src/main/assets/13.textproto b/domain/src/main/assets/13.textproto
index e57cd4464cc..f71a38838bd 100644
--- a/domain/src/main/assets/13.textproto
+++ b/domain/src/main/assets/13.textproto
@@ -15,6 +15,18 @@ states {
written_translations {
key: "content"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Este \303\251 o fim e nbsp;
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \331\207\330\260\331\207 \331\207\331\212 \330\247\331\204\331\206\331\207\330\247\331\212\330\251 & nbsp\330\233
"
+ }
+ }
}
}
interaction {
@@ -95,56 +107,188 @@ states {
written_translations {
key: "content"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Nosso sistema solar consiste em nove planetas orbitando em torno de nosso sol: Merc\303\272rio, Venus, Terra, Marte, J\303\272piter, Saturno, Urano, Netuno e Plut\303\243o. Eles variam de gigantes de g\303\241s enormes a pequenos planetas an\303\265es gelados.
Clique nos planetas abaixo para descobrir mais sobre cada um!
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \331\212\330\252\331\203\331\210\331\206 \331\206\330\270\330\247\331\205\331\206\330\247 \330\247\331\204\330\264\331\205\330\263\331\212 \331\205\331\206 \330\252\330\263\330\271\330\251 \331\203\331\210\330\247\331\203\330\250 \330\252\330\257\331\210\330\261 \330\255\331\210\331\204 \330\247\331\204\330\264\331\205\330\263: \330\247\331\204\330\262\330\246\330\250\331\202\330\214 \331\201\331\212\331\206\331\210\330\263\330\214 \330\247\331\204\330\243\330\261\330\266\330\214 \330\247\331\204\331\205\330\261\331\212\330\256\330\214 \331\203\331\210\331\203\330\250 \330\247\331\204\331\205\330\264\330\252\330\261\331\212\330\214 \330\262\330\255\331\204\330\214 \330\243\331\210\330\261\330\247\331\206\331\210\330\263\330\214 \331\206\330\250\330\252\331\210\331\206\330\214 \330\250\331\204\331\210\330\252\331\210. \331\210\331\207\331\212 \330\252\330\252\330\261\330\247\331\210\330\255 \330\250\331\212\331\206 \330\271\331\205\330\247\331\204\331\202\330\251 \330\247\331\204\330\272\330\247\330\262 \330\247\331\204\330\266\330\256\331\205\330\251 \330\245\331\204\331\211 \331\203\331\210\330\247\331\203\330\250 \330\247\331\204\330\243\331\202\330\262\331\205 \330\247\331\204\331\202\330\262\331\205 \330\247\331\204\330\265\330\272\331\212\330\261\330\251.
\330\247\331\206\331\202\330\261 \330\271\331\204\331\211 \330\247\331\204\331\203\331\210\330\247\331\203\330\250 \330\243\330\257\331\206\330\247\331\207 \331\204\331\205\330\271\330\261\331\201\330\251 \330\247\331\204\331\205\330\262\331\212\330\257 \330\271\331\206 \331\203\331\204 \331\210\330\247\330\255\330\257!
"
+ }
+ }
}
}
written_translations {
key: "feedback_2"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Este \303\251 venus, o segundo planeta do sol. \303\211 o planeta mais brilhante do sistema solar, e \303\240s vezes pode ser visto durante o dia. Embora o merc\303\272rio esteja mais perto do sol do que V\303\252nus, Venus \303\251 o planeta mais quente do sistema solar, porque sua atmosfera espessa de di\303\263xido de carbono traga calor na sua superf\303\255cie.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \331\207\330\260\330\247 \331\207\331\210 \331\201\331\212\331\206\331\210\330\263\330\214 \331\210\330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\253\330\247\331\206\331\212 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263. \330\245\331\206\331\207 \330\243\331\204\331\205\330\271 \331\203\331\210\331\203\330\250 \331\201\331\212 \330\247\331\204\331\206\330\270\330\247\331\205 \330\247\331\204\330\264\331\205\330\263\331\212\330\214 \331\210\331\212\331\205\331\203\331\206 \330\243\331\206 \331\212\331\206\330\270\330\261 \330\245\331\204\331\212\331\207 \331\201\331\212 \330\250\330\271\330\266 \330\247\331\204\330\243\330\255\331\212\330\247\331\206 \330\256\331\204\330\247\331\204 \330\247\331\204\331\212\331\210\331\205. \330\271\331\204\331\211 \330\247\331\204\330\261\330\272\331\205 \331\205\331\206 \330\243\331\206 \330\247\331\204\330\262\330\246\330\250\331\202 \330\243\331\202\330\261\330\250 \330\245\331\204\331\211 \330\247\331\204\330\264\331\205\330\263 \331\205\331\206 \331\201\331\212\331\206\331\210\330\263\330\214 \331\201\330\245\331\206 \331\201\331\212\331\206\331\210\330\263 \331\207\331\210 \330\263\330\256\331\210\331\206\330\251 \331\203\331\210\331\203\330\250 \331\201\331\212 \330\247\331\204\331\206\330\270\330\247\331\205 \330\247\331\204\330\264\331\205\330\263\331\212\330\214 \331\204\330\243\331\206 \330\254\331\210 \330\263\331\205\331\212\331\203\330\251 \330\253\330\247\331\206\331\212 \330\243\331\203\330\263\331\212\330\257 \330\247\331\204\331\203\330\261\330\250\331\210\331\206 \330\247\331\204\331\201\330\256\330\247\330\256 \330\247\331\204\330\255\330\261\330\247\330\261\330\251 \330\271\331\204\331\211 \330\263\330\267\330\255\331\207.
"
+ }
+ }
}
}
written_translations {
key: "feedback_3"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Terra! Nosso planeta de casa \303\251 o terceiro planeta do sol. Apesar da vastid\303\243o do sistema solar e do resto do espa\303\247o, \303\251 o \303\272nico lugar que sabemos que a vida existe. \303\211 apenas a dist\303\242ncia certa do sol para garantir uma temperatura confort\303\241vel para que existam.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \330\247\331\204\330\243\330\261\330\266! \331\203\331\210\331\203\330\250\331\206\330\247 \330\247\331\204\330\261\330\246\331\212\330\263\331\212 \331\207\331\210 \330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\253\330\247\331\204\330\253 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263. \330\271\331\204\331\211 \330\247\331\204\330\261\330\272\331\205 \331\205\331\206 \330\247\330\263\330\252\331\205\330\261\330\247\330\261 \330\247\331\204\331\206\330\270\330\247\331\205 \330\247\331\204\330\264\331\205\330\263\331\212 \331\210\330\250\331\202\331\212\330\251 \330\247\331\204\331\205\330\263\330\247\330\255\330\251\330\214 \331\201\330\245\331\206 \330\247\331\204\331\205\331\203\330\247\331\206 \330\247\331\204\331\210\330\255\331\212\330\257 \330\247\331\204\330\260\331\212 \331\206\330\271\330\261\331\201\331\207 \330\243\331\206 \330\247\331\204\330\255\331\212\330\247\330\251 \331\205\331\210\330\254\331\210\330\257\330\251. \331\201\331\212 \330\247\331\204\331\205\330\263\330\247\331\201\330\251 \330\247\331\204\331\212\331\205\331\206\331\211 \331\201\331\202\330\267 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263 \331\204\330\266\331\205\330\247\331\206 \330\257\330\261\330\254\330\251 \330\255\330\261\330\247\330\261\330\251 \331\205\330\261\331\212\330\255\330\251 \330\250\330\247\331\204\331\206\330\263\330\250\330\251 \331\204\331\206\330\247 \331\205\331\210\330\254\331\210\330\257\330\251 \331\201\331\212\331\207\330\247.
"
+ }
+ }
}
}
written_translations {
key: "feedback_1"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Isso \303\251 merc\303\272rio, um pequeno planeta met\303\241lico mais pr\303\263ximo do sol. Como \303\251 t\303\243o perto do sol, leva apenas 88 dias para viajar uma \303\263rbita inteira, ent\303\243o um ano em Merc\303\272rio s\303\263 seria de 88 dias.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: "\n Saturno, o sexto planeta do sol, \303\251 mais conhecido por seus belos an\303\251is de gelo e poeira em torno dele. Como J\303\272piter, \303\251 um gigante de g\303\241s, principalmente feito de camadas de hidrog\303\252nio que ficam progressivamente mais densos em rela\303\247\303\243o ao seu n\303\272cleo s\303\263lido. "
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " Saturn\330\214 \330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\263\330\247\330\257\330\263 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263\330\214 \331\207\331\210 \331\205\330\271\330\261\331\210\331\201 \330\254\331\212\330\257\330\247 \331\204\330\255\331\204\331\202\330\247\330\252\331\207\330\247 \330\247\331\204\330\254\331\205\331\212\331\204\330\251 \331\205\331\206 \330\247\331\204\330\254\331\204\331\212\330\257 \331\210\330\247\331\204\330\272\330\250\330\247\330\261 \330\247\331\204\331\205\330\257\330\247\330\261 \330\255\331\210\331\204\331\207\330\247. \331\205\330\253\331\204 \331\203\331\210\331\203\330\250 \330\247\331\204\331\205\330\264\330\252\330\261\331\212\330\214 \331\201\331\207\331\212 \330\271\331\205\331\204\330\247\331\202\330\251 \330\272\330\247\330\262\331\212\330\251\330\214 \331\205\330\271\330\270\331\205\331\207\330\247 \331\205\330\265\331\206\331\210\330\271\330\251 \331\205\331\206 \330\267\330\250\331\202\330\247\330\252 \330\247\331\204\331\207\331\212\330\257\330\261\331\210\330\254\331\212\331\206 \330\247\331\204\330\252\331\212 \330\252\330\255\330\265\331\204 \330\271\331\204\331\211 \331\203\330\253\330\247\331\201\330\251 \330\252\330\257\330\261\331\212\330\254\331\212\330\251 \331\206\330\255\331\210 \330\254\331\210\331\207\330\261\331\207\330\247 \330\247\331\204\330\265\331\204\330\250.
"
+ }
+ }
}
}
written_translations {
key: "feedback_7"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Urano \303\251 o s\303\251timo planeta do sol. Foi o primeiro planeta que foi descoberto usando um telesc\303\263pio, j\303\241 que \303\251 normalmente muito obscuro para ser visto com os olhos nus. \303\211 o planeta mais frio, e \303\251 formado de camadas de g\303\241s de hidrog\303\252nio envolvendo um n\303\272cleo fr\303\255gido de rocha e gelo.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \330\243\331\210\330\261\330\247\331\206\331\210\330\263 \331\207\331\210 \330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\263\330\247\330\250\330\271 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263. \331\203\330\247\331\206 \330\243\331\210\331\204 \331\203\331\210\331\203\330\250 \331\212\330\252\331\205 \330\247\331\203\330\252\330\264\330\247\331\201\331\207 \330\250\330\247\330\263\330\252\330\256\330\257\330\247\331\205 \330\247\331\204\330\252\331\204\330\263\331\203\331\210\330\250 \330\214 \331\204\330\243\331\206\331\207 \330\271\330\247\330\257\330\251 \331\205\330\247 \331\212\331\203\331\210\331\206 \330\256\330\247\331\201\330\252\331\213\330\247 \330\254\330\257\331\213\330\247 \330\250\330\255\331\212\330\253 \331\204\330\247 \331\212\331\205\331\203\331\206 \330\261\330\244\331\212\330\252\331\207 \330\250\330\247\331\204\330\271\331\212\331\206 \330\247\331\204\331\205\330\254\330\261\330\257\330\251. \330\245\331\206\331\207 \330\243\330\250\330\261\330\257 \331\203\331\210\331\203\330\250 \330\214 \331\210\331\212\330\252\331\203\331\210\331\206 \331\205\331\206 \330\267\330\250\331\202\330\247\330\252 \331\205\331\206 \330\272\330\247\330\262 \330\247\331\204\331\207\331\212\330\257\330\261\331\210\330\254\331\212\331\206 \330\252\330\255\331\212\330\267 \330\250\331\206\331\210\330\247\330\251 \331\205\330\252\330\254\331\205\330\257\330\251 \331\205\331\206 \330\247\331\204\330\265\330\256\331\210\330\261 \331\210\330\247\331\204\330\254\331\204\331\212\330\257.
"
+ }
+ }
}
}
written_translations {
key: "feedback_4"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Marte \303\251 o quarto planeta do sol. Muitas vezes \303\251 apelidado do \"Planeta Vermelho\" devido \303\240 sua cor, que \303\251 resultado de grandes quantidades de \303\263xido de ferro em sua superf\303\255cie. Tem uma atmosfera fina de di\303\263xido de carbono e c\303\241psulas de gelo em cada extremidade, e tem duas pequenas luas de forma irregular chamada Phobos e Deimos.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \330\247\331\204\331\205\330\261\331\212\330\256 \331\207\331\210 \330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\261\330\247\330\250\330\271 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263. \330\272\330\247\331\204\330\250\330\247 \331\205\330\247 \331\212\330\267\331\204\331\202 \330\271\331\204\331\212\331\207 \"\330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\243\330\255\331\205\330\261\" \330\250\330\263\330\250\330\250 \331\204\331\210\331\206\331\207\330\214 \331\210\331\207\331\210 \331\206\330\252\331\212\330\254\330\251 \331\203\331\205\331\212\330\247\330\252 \331\203\330\250\331\212\330\261\330\251 \331\205\331\206 \330\243\331\203\330\263\331\212\330\257 \330\247\331\204\330\255\330\257\331\212\330\257 \330\271\331\204\331\211 \330\263\330\267\330\255\331\207\330\247. \331\212\330\255\330\252\331\210\331\212 \330\271\331\204\331\211 \330\254\331\210 \330\261\331\202\331\212\331\202 \331\205\331\206 \330\253\330\247\331\206\331\212 \330\243\331\203\330\263\331\212\330\257 \330\247\331\204\331\203\330\261\330\250\331\210\331\206 \331\210\330\247\331\204\331\202\330\250\330\271\330\247\330\252 \330\247\331\204\330\254\331\204\331\212\330\257\331\212\330\251 \331\201\331\212 \330\243\331\212 \331\205\331\206 \330\247\331\204\330\267\330\261\331\201\331\212\331\206\330\214 \331\210\331\204\330\257\331\212\331\207 \330\247\330\253\331\206\331\212\331\206 \331\205\331\206 \330\243\331\202\331\205\330\247\330\261 \330\265\330\272\331\212\330\261\330\251 \330\271\331\204\331\211 \330\264\331\203\331\204 \330\272\331\212\330\261 \331\205\331\206\330\252\330\270\331\205 \331\212\330\263\331\205\331\211 \331\201\331\210\330\250\331\210\330\263 \331\210\330\257\331\212\331\205\331\210\330\263.
"
+ }
+ }
}
}
written_translations {
key: "feedback_5"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " J\303\272piter, o quinto planeta a partir do Sol, \303\251 um enorme planeta 2,5 vezes mais pesado do que todos os outros planetas do nosso Sistema Solar combinados. \303\211 principalmente formado por gases espessos em torno de um n\303\272cleo s\303\263lido. Uma caracter\303\255stica famosa \303\251 a \"Grande Mancha Vermelha\", que \303\251 uma enorme tempestade em sua superf\303\255cie que assola h\303\241 pelo menos 350 anos e \303\251 grande o suficiente para engolir tr\303\252s Terras inteiras.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \331\203\331\210\331\203\330\250 \330\247\331\204\331\205\330\264\330\252\330\261\331\212\330\214 \330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\256\330\247\331\205\330\263 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263\330\214 \331\207\331\210 \331\203\331\210\331\203\330\250 \330\266\330\256\331\205 \331\212\330\250\331\204\330\272 \331\205\331\206 \330\247\331\204\330\271\331\205\330\261 2.5 \331\205\330\261\330\251 \331\205\331\206 \331\203\331\204 \330\247\331\204\331\203\331\210\330\247\331\203\330\250 \330\247\331\204\330\243\330\256\330\261\331\211 \331\201\331\212 \331\206\330\270\330\247\331\205\331\206\330\247 \330\247\331\204\330\264\331\205\330\263\331\212 \331\205\330\254\330\252\331\205\330\271\330\251. \331\212\330\252\331\205 \330\252\330\264\331\203\331\212\331\204\331\207\330\247 \331\201\331\212 \330\247\331\204\330\272\330\247\331\204\330\250 \331\205\331\206 \330\247\331\204\330\272\330\247\330\262\330\247\330\252 \330\247\331\204\330\263\331\205\331\212\331\203\330\251 \330\255\331\210\331\204 \330\254\331\210\331\207\330\261 \330\265\331\204\330\250. \331\205\331\212\330\262\330\251 \331\210\330\247\330\255\330\257\330\251 \331\205\330\264\331\207\331\210\330\261\330\251 \330\250\331\207\330\247 \331\207\331\212 \"\330\250\331\202\330\271\330\251 \330\255\331\205\330\261\330\247\330\241 \330\261\330\247\330\246\330\271\330\251\"\330\214 \331\210\331\207\331\212 \330\271\330\247\330\265\331\201\330\251 \331\203\330\250\331\212\330\261\330\251 \330\271\331\204\331\211 \330\263\330\267\330\255\331\207\330\247 \330\247\331\204\330\260\331\212 \330\247\331\206\330\257\331\204\330\271\330\252 \331\204\331\205\330\257\330\251 350 \330\271\330\247\331\205\330\247 \330\271\331\204\331\211 \330\247\331\204\330\243\331\202\331\204\330\214 \331\210\331\207\331\210 \331\203\330\250\331\212\330\261 \330\250\331\205\330\247 \331\212\331\203\331\201\331\212 \331\204\330\247\330\250\330\252\331\204\330\247\330\271 \330\253\331\204\330\247\330\253\330\251 \330\243\330\260\330\261\330\271 \330\250\330\243\331\203\331\205\331\204\331\207\330\247.
"
+ }
+ }
}
}
written_translations {
key: "default_outcome"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Isso n\303\243o parece ser a escolha correta.Tente novamente
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \331\207\330\260\330\247 \331\204\330\247 \331\212\330\250\330\257\331\210 \331\210\331\203\330\243\331\206\331\207 \330\247\331\204\330\247\330\256\330\252\331\212\330\247\330\261 \330\247\331\204\330\265\330\255\331\212\330\255. \331\205\330\261\330\251 \330\243\330\256\330\261\331\211
"
+ }
+ }
}
}
written_translations {
key: "feedback_8"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Netuno \303\251 o oitavo planeta do sol. \303\211 um \"gigante de gelo\", como Urano, com uma camada externa de hidrog\303\252nio, h\303\251lio e metano envolvendo um n\303\272cleo gelado. O metano em sua atmosfera absorve a luz vermelha, dando-lhe uma bela cor azul. Foi previsto primeiro existir de c\303\241lculos gravitacionais, e s\303\263 foi descoberto pelo telesc\303\263pio mais tarde.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \331\206\330\250\330\252\331\210\331\206 \331\207\331\210 \330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\253\330\247\331\205\331\206 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263. \330\245\331\206\331\207 \"\330\271\331\205\331\204\330\247\331\202 \330\253\331\204\330\254\"\330\214 \331\205\330\253\331\204 \330\243\331\210\330\261\330\247\331\206\331\210\330\263\330\214 \330\250\330\267\330\250\331\202\330\251 \330\256\330\247\330\261\330\254\331\212\330\251 \331\205\331\206 \330\247\331\204\331\207\331\212\330\257\330\261\331\210\330\254\331\212\331\206 \331\210\330\247\331\204\331\207\331\212\331\204\331\212\331\210\331\205 \331\210\330\247\331\204\331\205\331\212\330\253\330\247\331\206 \331\212\330\272\331\204\331\212\331\201 \330\254\331\210\331\207\330\261 \330\254\331\204\331\212\330\257\331\212. \331\212\331\205\330\252\330\265 \330\247\331\204\331\205\331\212\330\253\330\247\331\206 \331\201\331\212 \330\254\331\210\331\207\330\247 \330\247\331\204\330\266\331\210\330\241 \330\247\331\204\330\243\330\255\331\205\330\261\330\214 \331\205\331\205\330\247 \331\212\331\205\331\206\330\255\331\207 \330\247\331\204\331\204\331\210\331\206 \330\247\331\204\330\243\330\262\330\261\331\202 \330\247\331\204\330\254\331\205\331\212\331\204. \330\252\331\205 \330\252\331\210\331\202\330\271 \331\210\330\254\331\210\330\257\331\207 \331\204\330\243\331\210\331\204 \331\205\330\261\330\251 \331\205\331\206 \330\255\330\263\330\247\330\250\330\247\330\252 \330\247\331\204\330\254\330\247\330\260\330\250\331\212\330\251\330\214 \331\210\330\252\331\205 \330\247\331\203\330\252\330\264\330\247\331\201\331\207 \331\201\331\202\330\267 \330\250\331\210\330\247\330\263\330\267\330\251 \330\247\331\204\330\252\331\204\330\263\331\203\331\210\330\250 \331\201\331\212 \331\210\331\202\330\252 \331\204\330\247\330\255\331\202.
"
+ }
+ }
}
}
written_translations {
key: "feedback_9"
value {
+ translation_mapping {
+ key: "pt"
+ value {
+ html: " Plut\303\243o j\303\241 foi considerado o nono planeta do sol, mas agora foi reclassificado como um \"planeta an\303\243o\". \303\211 formado por gelo e rocha, e \303\251 ainda menor que a nossa lua. A primeira espa\303\247onave a visitar Plut\303\243o \303\251 devido ao alcance em julho de 2015.
"
+ }
+ }
+ translation_mapping {
+ key: "ar"
+ value {
+ html: " \331\203\330\247\331\206 \330\250\331\204\331\210\330\252\331\210 \331\212\330\271\330\252\330\250\330\261 \331\205\330\261\330\251 \331\210\330\247\330\255\330\257\330\251 \330\247\331\204\331\203\331\210\331\203\330\250 \330\247\331\204\330\252\330\247\330\263\330\271 \331\205\331\206 \330\247\331\204\330\264\331\205\330\263\330\214 \331\204\331\203\331\206 \330\252\331\205 \330\245\330\271\330\247\330\257\330\251 \330\252\330\265\331\206\331\212\331\201\331\207 \330\247\331\204\330\242\331\206 \330\250\330\247\330\271\330\252\330\250\330\247\330\261\331\207 \"\331\203\331\210\331\203\330\250 \331\202\330\262\331\205\". \331\212\330\252\331\205 \330\252\330\264\331\203\331\212\331\204\331\207 \331\205\331\206 \330\247\331\204\330\254\331\204\331\212\330\257 \331\210\330\247\331\204\330\265\330\256\331\210\330\261\330\214 \331\210\331\207\331\210 \330\243\330\265\330\272\330\261 \331\205\331\206 \331\202\331\205\330\261\331\206\330\247. \331\205\331\206 \330\247\331\204\331\205\331\202\330\261\330\261 \330\243\331\206 \331\212\330\265\331\204 \330\247\331\204\331\205\330\261\331\203\330\250\330\251 \330\247\331\204\331\201\330\266\330\247\330\246\331\212\330\251 \330\247\331\204\330\243\331\210\331\204\331\211 \330\245\331\204\331\211 \330\262\331\212\330\247\330\261\330\251 \330\250\331\204\331\210\330\252\331\210 \331\201\331\212 \331\212\331\210\331\204\331\212\331\210 2015.
"
+ }
+ }
}
}
interaction {
diff --git a/domain/src/main/assets/2mzzFVDLuAj8.json b/domain/src/main/assets/2mzzFVDLuAj8.json
index 091c88ad932..acb06d246f8 100644
--- a/domain/src/main/assets/2mzzFVDLuAj8.json
+++ b/domain/src/main/assets/2mzzFVDLuAj8.json
@@ -19,7 +19,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -33,7 +33,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "71b5c0d8-2c30-4bf8-ac21-dd42774c4b95",
"normalizedStrSet": ["2:1"]
}
}
@@ -46,16 +46,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "4d5dbaff-8f03-4b9e-b3e6-241ec4a8827b",
"normalizedStrSet": ["3"]
}
}
@@ -68,16 +68,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "4992c409-75ec-4729-b7f0-d4a7d18b5c92",
"normalizedStrSet": ["1:2"]
}
}
@@ -90,10 +90,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "writing ratio of 2 quanitites",
@@ -103,13 +103,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -188,36 +188,36 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cUncle Berry,\u201d James ne kaha, \u201cMujhe ek mixed-fruit smoothie banani hai! Mujhe lagta hai yeh baqi smoothies se zyadah mazedaar hogi.\u201d
\n\n\u201cZarur!\u201d Uncle Berry ne kaha. \u201cTum kaun se phal milana chahte ho?\u201d
\n\n\u201cMujhe strawberries aur aam pasand hain!\u201d James ne kaha, smoothie ke baare mein sochkar muskuraate hue.
\n\n\u201cTheek hai,\u201d Uncle Berry ne kaha, kitaab mein recipe dhoondte hue. \u201cHamein aam, strawberry, aur doodh daalna hoga, is tasveer ko dekhte hue.\u201d
\n \n\n
\n\n\u201cAb,\u201d unhone kaha, \u201ckya tum mujhe yeh bata sakte ho ki aam se strawberry ya kya ratio is recipe mein?\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cUncle Berry,\u201d James ne kaha, \u201cMujhe ek mixed-fruit smoothie banani hai! Mujhe lagta hai yeh baqi smoothies se zyadah mazedaar hogi.\u201d
\n\n\u201cZarur!\u201d Uncle Berry ne kaha. \u201cTum kaun se phal milana chahte ho?\u201d
\n\n\u201cMujhe strawberries aur aam pasand hain!\u201d James ne kaha, smoothie ke baare mein sochkar muskuraate hue.
\n\n\u201cTheek hai,\u201d Uncle Berry ne kaha, kitaab mein recipe dhoondte hue. \u201cHamein aam, strawberry, aur doodh daalna hoga, is tasveer ko dekhte hue.\u201d
\n \n\n
\n\n\u201cAb,\u201d unhone kaha, \u201ckya tum mujhe yeh bata sakte ho ki aam se strawberry ya kya ratio is recipe mein?\u201d
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Uncle berry aam se strawberry ka ratio chaah rahe hain. Toh tum bas un dono phal par dhyaan de sakte ho, aur doodh ko dhyaan na do.
\n\nPhir se koshish karo!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Uncle berry aam se strawberry ka ratio chaah rahe hain. Toh tum bas un dono phal par dhyaan de sakte ho, aur doodh ko dhyaan na do.
\n\nPhir se koshish karo!
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Han, yeh sahi hai. Bahut ache!
",
+ "data_format": "html",
+ "translation": {"translation" : "Han, yeh sahi hai. Bahut ache!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Yeh sawaal sirf aam aur strawberry ke ratio ke baare mein pooch raha hai, toh tumhein doodh ke baare mein jaankari ki zarurat nahin hai. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Yeh sawaal sirf aam aur strawberry ke ratio ke baare mein pooch raha hai, toh tumhein doodh ke baare mein jaankari ki zarurat nahin hai. Phir se koshish kariye.
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Tumne number sahi likhe hain, likin yaad rakho ki order se faraq padta hai! Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Tumne number sahi likhe hain, likin yaad rakho ki order se faraq padta hai! Phir se koshish kariye.
"},
"needs_update": false
}
}
@@ -236,7 +236,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -250,7 +250,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "8d2b9eea-c973-4bd6-a7e0-b1a7ae5603be",
"normalizedStrSet": ["2:3"]
}
}
@@ -263,16 +263,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "c3c47717-3fdb-4d8d-9bfe-3dc72d19b8da",
"normalizedStrSet": ["2 to 3"]
}
}
@@ -285,16 +285,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "a2431f83-2e3a-433c-8863-fe9dc3a75b98",
"normalizedStrSet": ["3:2"]
}
}
@@ -307,16 +307,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "428fab4b-2a57-40e5-b025-9ffc11def1f4",
"normalizedStrSet": ["1"]
}
}
@@ -329,10 +329,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 10",
@@ -342,8 +342,8 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [{
"hint_content": {
@@ -353,7 +353,7 @@
}],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -460,50 +460,50 @@
"translations_mapping": {
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "\u201cYeh sahi hai! Magar apna jawaab X:Y ki shakal mein likhna, jahan X aur Y number hain. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cYeh sahi hai! Magar apna jawaab X:Y ki shakal mein likhna, jahan X aur Y number hain. Phir se koshish kariye.
"},
"needs_update": false
}
},
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cSahi kaha!\u201d kaha Uncle Berry ne. \u201cJab aapko sirf kuch hi cheezon ki ratio nikalni ho, aapko baqiyon pe dhyaan dene ki zarurat nahin hai.\u201d
\n\nJames ne aam aur strawberry milaye. Tab usko ek idea aaya. \u201cUncle Berry,\u201d usne kaha, \u201ckya main smoothie mein blueberry bhi daal sakta hun?\u201d
\n\n\u201cHan, zarur!\u201d Uncle Berry ne kaha. \u201cTumko kitna chahiye?\u201d
\n\nJames ne kitaab mein blueberry ki drawing banakar Uncle Berry ko dikhayi.
\n\n
\n \n\n
\n\n\u201cAcha lag raha hai, James! Uncle Berry ne kaha. \u201cAb, tumhare naye recipe mein, kya tum blueberry se doodh ka ratio dhoond sakte ho?\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cSahi kaha!\u201d kaha Uncle Berry ne. \u201cJab aapko sirf kuch hi cheezon ki ratio nikalni ho, aapko baqiyon pe dhyaan dene ki zarurat nahin hai.\u201d
\n\nJames ne aam aur strawberry milaye. Tab usko ek idea aaya. \u201cUncle Berry,\u201d usne kaha, \u201ckya main smoothie mein blueberry bhi daal sakta hun?\u201d
\n\n\u201cHan, zarur!\u201d Uncle Berry ne kaha. \u201cTumko kitna chahiye?\u201d
\n\nJames ne kitaab mein blueberry ki drawing banakar Uncle Berry ko dikhayi.
\n\n
\n \n\n
\n\n\u201cAcha lag raha hai, James! Uncle Berry ne kaha. \u201cAb, tumhare naye recipe mein, kya tum blueberry se doodh ka ratio dhoond sakte ho?\u201d
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Yaad rakho ki, ek ratio mein, number sahi order mein hone chahiyen. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Yaad rakho ki, ek ratio mein, number sahi order mein hone chahiyen. Phir se koshish kariye.
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Sahi!
",
+ "data_format": "html",
+ "translation": {"translation" : "Sahi!
"},
"needs_update": false
}
},
"hint_1": {
"hi": {
- "data_format": "",
- "translation": "Kyunki tumne blueberry se doodh ka ratio poocha hai, tumhein sirf in dono cheezen pe dhyaan dene ki zarurat hai. Tum baqi pe dhyaan na do!
",
+ "data_format": "html",
+ "translation": {"translation" : "Kyunki tumne blueberry se doodh ka ratio poocha hai, tumhein sirf in dono cheezen pe dhyaan dene ki zarurat hai. Tum baqi pe dhyaan na do!
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Tumhein sirf blueberry aur doodh pe dhyaan dekar baqi saare phal pe dhyaan nahin dena chahiye.
\n \n\nPhir se koshish kariye. Tum kar sakte ho!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Tumhein sirf blueberry aur doodh pe dhyaan dekar baqi saare phal pe dhyaan nahin dena chahiye.
\n \n\nPhir se koshish kariye. Tum kar sakte ho!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Yaad rakhiye, tumhein bas blueberry se doodh ka ratio dhoondna hoga. Tum baqi cheezen chod sakte ho.
\n \n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Yaad rakhiye, tumhein bas blueberry se doodh ka ratio dhoondna hoga. Tum baqi cheezen chod sakte ho.
\n \n\nPhir se koshish kariye!
"},
"needs_update": false
}
}
@@ -534,13 +534,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -570,8 +570,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "James se milo!
\n \n\nJames ko apne doston ke saath khelna, aur tasty khana khana pasand hai. Ham log dekhenge ki James kaise ratios seekhta aur istemaal karta hai.
\n\nAage badhne se pahle aapko yeh sab cheezen pata honi chahiye:
\n\n\n\t\n\tCounting numbers (1, 2, 3, 4, 5, \u2026) ko add, subtract, multiply, aur divide karna
\n\t \n\t\n\tYeh samajh ki ek counting number doosre counting number se bada ya chota hai
\n\t \n \n\nAap paper aur pen lekar kaam karne ke liye shaant jagah bhi dhoond lo. Aapko story samajhne ke liye jitne waqt ki zarurat hai lijiye, aur sukoon se story padhiye. Samajh tezi se zyadah zaruri hai!
\n\nJab aap shuroo karne ke liye tayyaar ho, tab Continue click karo.
",
+ "data_format": "html",
+ "translation": {"translation" : "James se milo!
\n \n\nJames ko apne doston ke saath khelna, aur tasty khana khana pasand hai. Ham log dekhenge ki James kaise ratios seekhta aur istemaal karta hai.
\n\nAage badhne se pahle aapko yeh sab cheezen pata honi chahiye:
\n\n\n\t\n\tCounting numbers (1, 2, 3, 4, 5, \u2026) ko add, subtract, multiply, aur divide karna
\n\t \n\t\n\tYeh samajh ki ek counting number doosre counting number se bada ya chota hai
\n\t \n \n\nAap paper aur pen lekar kaam karne ke liye shaant jagah bhi dhoond lo. Aapko story samajhne ke liye jitne waqt ki zarurat hai lijiye, aur sukoon se story padhiye. Samajh tezi se zyadah zaruri hai!
\n\nJab aap shuroo karne ke liye tayyaar ho, tab Continue click karo.
"},
"needs_update": false
}
},
@@ -591,7 +591,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -605,7 +605,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "20c3a4f5-b4c7-4fe6-9780-f7e01b0e532c",
"normalizedStrSet": ["2:4:3:2"]
}
}
@@ -618,16 +618,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "75b41c64-7309-4a2e-a78e-45a0ca35ffab",
"normalizedStrSet": ["2:4:3:1"]
}
}
@@ -640,10 +640,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Final Test",
@@ -653,8 +653,8 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [{
"hint_content": {
@@ -664,7 +664,7 @@
}],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -743,36 +743,36 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "James aur Uncle Berry ne mixed-fruit smoothie banaya, aur woh bahut swaadisht tha!
\n\n\u201cBas aaj ke liye ek aur karte hain!\u201d James ne kaha. Usne ek nayi recipe ki taraf ishara kiya.
\n\nUncle Berry ne recipe ko zor se padha. \u201cIs recipe ke hisaab se, har 2 unit doodh ke liye, hamein 4 unit blueberry, 2 unit strawberry, aur 1 unit kela chahiye.\u201d
\n\n\u201cOh nahin,\u201d James ne kaha. \u201cAaj ke liye bahut kele kha liye. Kya ham kele ki jagah 2 unit aam instemaal kar sakte hain?\u201d James ne poocha.
\n\n\u201cHan, tum yeh kar sakte ho!\u201d Uncle Berry ne kaha. \u201cTab phir batao, ki doodh se blueberry se strawberry se aam ka kya ratio hoga?\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "James aur Uncle Berry ne mixed-fruit smoothie banaya, aur woh bahut swaadisht tha!
\n\n\u201cBas aaj ke liye ek aur karte hain!\u201d James ne kaha. Usne ek nayi recipe ki taraf ishara kiya.
\n\nUncle Berry ne recipe ko zor se padha. \u201cIs recipe ke hisaab se, har 2 unit doodh ke liye, hamein 4 unit blueberry, 2 unit strawberry, aur 1 unit kela chahiye.\u201d
\n\n\u201cOh nahin,\u201d James ne kaha. \u201cAaj ke liye bahut kele kha liye. Kya ham kele ki jagah 2 unit aam instemaal kar sakte hain?\u201d James ne poocha.
\n\n\u201cHan, tum yeh kar sakte ho!\u201d Uncle Berry ne kaha. \u201cTab phir batao, ki doodh se blueberry se strawberry se aam ka kya ratio hoga?\u201d
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Tumne doodh se blueberry se strawberry se kele ka ratio likha hai, lekin yeh sawaal kuch aur pooch raha hai. Phir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Tumne doodh se blueberry se strawberry se kele ka ratio likha hai, lekin yeh sawaal kuch aur pooch raha hai. Phir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Acha kiya. Tumne sahi jawaab nikal liya hai! Ab James apni smoothie bana sakta hai :-)
",
+ "data_format": "html",
+ "translation": {"translation" : "Acha kiya. Tumne sahi jawaab nikal liya hai! Ab James apni smoothie bana sakta hai :-)
"},
"needs_update": false
}
},
"hint_1": {
"hi": {
- "data_format": "",
- "translation": "Yeh sawaal tumse chaar ingredients ke baare mein pooch raha hai, toh tumhare ratio mein chaar hisse honen chahiye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Yeh sawaal tumse chaar ingredients ke baare mein pooch raha hai, toh tumhare ratio mein chaar hisse honen chahiye.
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Phir se koshish kariye - tum kar sakte ho!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Phir se koshish kariye - tum kar sakte ho!
"},
"needs_update": false
}
}
@@ -791,7 +791,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -805,7 +805,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "fe13e3d8-4fc6-4863-a3ce-98c710a1ec82",
"normalizedStrSet": ["2:1:3"]
}
}
@@ -818,16 +818,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "cb9437b2-2931-4cc7-9cbe-ecb31ac22ad0",
"normalizedStrSet": ["2:1"]
}
}
@@ -840,16 +840,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "9ce00383-554d-4606-abcb-31d020799768",
"normalizedStrSet": ["1:3"]
}
}
@@ -862,16 +862,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "0fe368d4-5459-4535-94de-4c311afbfda1",
"normalizedStrSet": ["1:2:3"]
}
}
@@ -879,7 +879,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "b9eb65eb-8f9f-4c5b-80cf-c92c5fa6141f",
"normalizedStrSet": ["1:3:2"]
}
}
@@ -887,7 +887,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "468469f4-2a78-4e6c-abff-5004a158eae5",
"normalizedStrSet": ["2:3:1"]
}
}
@@ -895,7 +895,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "d61214b1-8e64-4f9a-bdcb-3f0c56e60960",
"normalizedStrSet": ["3:1:2"]
}
}
@@ -903,7 +903,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "150ad47d-cdce-4292-a2ac-17075efeb4a4",
"normalizedStrSet": ["3:2:1"]
}
}
@@ -916,10 +916,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Writing ratio with 3 quantities",
@@ -929,13 +929,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"feedback_2": {
@@ -1028,43 +1028,43 @@
"translations_mapping": {
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Tumne sirf aam aur strawberry ka ratio likha hai. Lekin sawaal dhyaan se padho \u2014 aap doodh ke baare mein bhool gaye hain! Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Tumne sirf aam aur strawberry ka ratio likha hai. Lekin sawaal dhyaan se padho \u2014 aap doodh ke baare mein bhool gaye hain! Phir se koshish kariye.
"},
"needs_update": false
}
},
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cBahut ache!\u201d Uncle Berry ne kaha.
\n\n\u201cTumhein bas ab ek aur cheez pata honi chahiye,\u201d unhone kaha. \u201cHam log do se zyadah cheezon ke liye bhi ratios likh sakte hain. Toh jaise, agar ham 3 litre blueberry, 2 litre kele ka paste, aur 4 litre doodh milayenge, toh blueberry se kele se doodh ka ratio 3:2:4 hoga.\u201d
\n \n\n
\n\n\u201cAb tum koshish karo. Kya tum is tasveer ke hisaab se aam se strawberry se doodh ka ratio likh sakte ho?\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cBahut ache!\u201d Uncle Berry ne kaha.
\n\n\u201cTumhein bas ab ek aur cheez pata honi chahiye,\u201d unhone kaha. \u201cHam log do se zyadah cheezon ke liye bhi ratios likh sakte hain. Toh jaise, agar ham 3 litre blueberry, 2 litre kele ka paste, aur 4 litre doodh milayenge, toh blueberry se kele se doodh ka ratio 3:2:4 hoga.\u201d
\n \n\n
\n\n\u201cAb tum koshish karo. Kya tum is tasveer ke hisaab se aam se strawberry se doodh ka ratio likh sakte ho?\u201d
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Tumne sirf strawberry aur doodh ka ratio likha hai. Lekin sawaal dhyaan se padho \u2014 aap aam ke baare mein bhool gaye hain! Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Tumne sirf strawberry aur doodh ka ratio likha hai. Lekin sawaal dhyaan se padho \u2014 aap aam ke baare mein bhool gaye hain! Phir se koshish kariye.
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Acha kiya! Yeh sahi jawaab hai.
",
+ "data_format": "html",
+ "translation": {"translation" : "Acha kiya! Yeh sahi jawaab hai.
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": " Nahin, yeh sahi nahin hai. Yaad rakho ki order sahi hona chahiye. Tumhara ratio sawaal mein diye order ke jaisa hona chahiye. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : " Nahin, yeh sahi nahin hai. Yaad rakho ki order sahi hona chahiye. Tumhara ratio sawaal mein diye order ke jaisa hona chahiye. Phir se koshish kariye.
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Phir se koshish kariye.
"},
"needs_update": false
}
}
@@ -1095,13 +1095,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -1125,8 +1125,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "Chalo ratios par ek aur nazar daalen.
\n\n--------------------------------------------------------------------------------------------------------------
\n\nRatio do aur zyadah cheezon ki relative ginti batata hai, yaani in cheezon ki ginti ek doosre ke muqaable batata hai.
\n\nMisaal ke taur par yeh sochte hain ki James khilaune aur aam 1:2 ki ratio mein badalta hai.
\n\n\n\t\n\tIska matlab hai, usko har khilaune ke badle mein 2 aam milenge.
\n\t \n\t\n\tToh, agar woh 1 khilauna dega, toh uske wapas 2 aam milenge.
\n\t \n\t\n\tAur, agar woh 3 khilaune dega, tab usko badle mein 6 aam milenge. (Yeh abhi bhi 1:2 ratio ki pairwee karega, kyunki har baar woh ek khilauna dega, usko wapas 2 aam milenge.)
\n\t \n \n\nNotice karo ki \u201c1:2\u201d tumhein khilaune aur aam ke beech ke rishte ke baare mein batata hai. Yeh tumhein yeh nahin batata hai ki James kitne samaan ki adla badli karega. Lekin agar tumhein yeh pata ho ki woh kitne khilaune dega, toh tum yeh pata laga sakte ho ki woh kitne aam ke liye badlega.
\n\n--------------------------------------------------------------------------------------------------------------
\n\nKya tumhein yeh samajh aaya? Tumhein yeh samajh aana chahiye ki kyun James ko 6 aam milenge 3 khilaune ke badle. Jab samajh aa jaye, \u201cContinue\u201d dabakar James aur Uncle Berry ki taraf laut jao.
",
+ "data_format": "html",
+ "translation": {"translation" : "Chalo ratios par ek aur nazar daalen.
\n\n--------------------------------------------------------------------------------------------------------------
\n\nRatio do aur zyadah cheezon ki relative ginti batata hai, yaani in cheezon ki ginti ek doosre ke muqaable batata hai.
\n\nMisaal ke taur par yeh sochte hain ki James khilaune aur aam 1:2 ki ratio mein badalta hai.
\n\n\n\t\n\tIska matlab hai, usko har khilaune ke badle mein 2 aam milenge.
\n\t \n\t\n\tToh, agar woh 1 khilauna dega, toh uske wapas 2 aam milenge.
\n\t \n\t\n\tAur, agar woh 3 khilaune dega, tab usko badle mein 6 aam milenge. (Yeh abhi bhi 1:2 ratio ki pairwee karega, kyunki har baar woh ek khilauna dega, usko wapas 2 aam milenge.)
\n\t \n \n\nNotice karo ki \u201c1:2\u201d tumhein khilaune aur aam ke beech ke rishte ke baare mein batata hai. Yeh tumhein yeh nahin batata hai ki James kitne samaan ki adla badli karega. Lekin agar tumhein yeh pata ho ki woh kitne khilaune dega, toh tum yeh pata laga sakte ho ki woh kitne aam ke liye badlega.
\n\n--------------------------------------------------------------------------------------------------------------
\n\nKya tumhein yeh samajh aaya? Tumhein yeh samajh aana chahiye ki kyun James ko 6 aam milenge 3 khilaune ke badle. Jab samajh aa jaye, \u201cContinue\u201d dabakar James aur Uncle Berry ki taraf laut jao.
"},
"needs_update": false
}
},
@@ -1146,7 +1146,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -1160,7 +1160,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "866a20f8-a118-4058-933b-b6a91afd73b2",
"normalizedStrSet": ["2:3"]
}
}
@@ -1173,16 +1173,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "75c4e32d-2357-46c8-8a1c-74dce83593fa",
"normalizedStrSet": ["2 : 3"]
}
}
@@ -1190,7 +1190,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "fb41c73e-a2e7-47ea-90f2-26319ca611cc",
"normalizedStrSet": ["2: 3"]
}
}
@@ -1198,7 +1198,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "156e3d22-7dfa-457d-b1d2-7ab26366349b",
"normalizedStrSet": ["2 :3"]
}
}
@@ -1211,16 +1211,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "c33e0c96-28f8-4656-8760-f9684f61469f",
"normalizedStrSet": ["3:2"]
}
}
@@ -1233,16 +1233,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "412b761e-74df-4161-a122-ad3bbe3908c2",
"normalizedStrSet": ["to"]
}
}
@@ -1255,16 +1255,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "ab884bed-5685-4031-b9d2-0dd085b43f50",
"normalizedStrSet": ["X"]
}
}
@@ -1272,7 +1272,7 @@
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "068691b9-2fcd-4d50-987b-a8e588b0a0c8",
"normalizedStrSet": ["Y"]
}
}
@@ -1285,16 +1285,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "763bf316-e64c-4196-993d-71df36487bdd",
"normalizedStrSet": [":"]
}
}
@@ -1307,10 +1307,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Writing ratio in colon notation",
@@ -1320,13 +1320,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -1447,57 +1447,57 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cSahi kaha!\u201d Uncle Berry ne bola.
\n\n\u201cWaise, James, tum ratios ko aur asaani se bhi likh sakte ho, ek colon (\u2018:\u2019) istemaal karke, \u2018to\u2019 ke bajaye. Toh, \u20183 to 1\u2019 likhne ki jagah, ham \u20183:1\u2019 likh sakte hain. Lekin jab isko padhoge phir bhi \u20183 to 1\u2019 kahoge.\u201d
\n\n\u201cAha!\u201d James ne kaha. \u201cChalo koshish karta hun. Main 2 toy car ke badle 3 kele badlunga.\u201d
\n\n\u201cAcha,\u201d Uncle Berry ne kaha. \u201cToh batao, is maamle mein toy car to kele ka ratio kya hoga?\u201d
\n\n(Ab se, apne saare ratios colon se hi likhna.)
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cSahi kaha!\u201d Uncle Berry ne bola.
\n\n\u201cWaise, James, tum ratios ko aur asaani se bhi likh sakte ho, ek colon (\u2018:\u2019) istemaal karke, \u2018to\u2019 ke bajaye. Toh, \u20183 to 1\u2019 likhne ki jagah, ham \u20183:1\u2019 likh sakte hain. Lekin jab isko padhoge phir bhi \u20183 to 1\u2019 kahoge.\u201d
\n\n\u201cAha!\u201d James ne kaha. \u201cChalo koshish karta hun. Main 2 toy car ke badle 3 kele badlunga.\u201d
\n\n\u201cAcha,\u201d Uncle Berry ne kaha. \u201cToh batao, is maamle mein toy car to kele ka ratio kya hoga?\u201d
\n\n(Ab se, apne saare ratios colon se hi likhna.)
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Sawaal dhyaan se padhiye! Aapke jawaab ko \u201cX:Y\u201d ki shakal mein hona chahiye, jahan X aur Y numbers hain.
\n\nPhir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Sawaal dhyaan se padhiye! Aapke jawaab ko \u201cX:Y\u201d ki shakal mein hona chahiye, jahan X aur Y numbers hain.
\n\nPhir se koshish kariye.
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi jawaab nahin hai. Numbers ki arrangement ka dhyaan rakhiye.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi jawaab nahin hai. Numbers ki arrangement ka dhyaan rakhiye.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Han sahi kaha!
\n\nBas ek cheez: jab ratios type kar rahe ho, tab number aur colon (\u2018:\u2019) ke beech mein koi space nahin likhna. Jaise, tumhein \u201c2:3\u201d type karna chahiye, na ki \u201c2 : 3\u201d ya \u201c2: 3\u201d.
",
+ "data_format": "html",
+ "translation": {"translation" : "Han sahi kaha!
\n\nBas ek cheez: jab ratios type kar rahe ho, tab number aur colon (\u2018:\u2019) ke beech mein koi space nahin likhna. Jaise, tumhein \u201c2:3\u201d type karna chahiye, na ki \u201c2 : 3\u201d ya \u201c2: 3\u201d.
"},
"needs_update": false
}
},
"feedback_6": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. James kitni adla badli kar raha hai, us pe dhyaan dijiye.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. James kitni adla badli kar raha hai, us pe dhyaan dijiye.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_7": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Aapko X aur Y mein sahi numbers likhne honge taaki ratio sahi nikle.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Aapko X aur Y mein sahi numbers likhne honge taaki ratio sahi nikle.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_5": {
"hi": {
- "data_format": "",
- "translation": "Sahi kaha!
",
+ "data_format": "html",
+ "translation": {"translation" : "Sahi kaha!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Aapke jawaab ko \u201cX:Y\u201d ki shakal mein hona chahiye, jahan X aur Y numbers hain.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Aapke jawaab ko \u201cX:Y\u201d ki shakal mein hona chahiye, jahan X aur Y numbers hain.
"},
"needs_update": false
}
}
@@ -1524,7 +1524,7 @@
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -1547,8 +1547,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "Mubarak ho is lesson ko khatam karne pe! Hamein ummeed hai ki tumhein James aur Uncle Berry ke saath smoothies banane mein maza aaya hoga.
\n\nJab tum tayyar ho, tab agle lesson pe badh jao dekhne ke liye ki James ne agle din kya seekha!
",
+ "data_format": "html",
+ "translation": {"translation" : "Mubarak ho is lesson ko khatam karne pe! Hamein ummeed hai ki tumhein James aur Uncle Berry ke saath smoothies banane mein maza aaya hoga.
\n\nJab tum tayyar ho, tab agle lesson pe badh jao dekhne ke liye ki James ne agle din kya seekha!
"},
"needs_update": false
}
}
@@ -1579,13 +1579,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -1609,8 +1609,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "Uncle Berry ne kaha, \u201cHan, maine kaha tha ki ratio kayi cheezon ke raqam ki beech relative rishta batata hai. Ab main tumko bataunga ki \u2018relative \u2019 lafz ka kya matlab hai.\u201d
\n \n\n
\n\n\u201cIn glasses ko dekh rahe ho? Yahan, doodh se aam ka ratio 1:2 hai, kyunki aap jitna bhi doodh lo, uske dugne aam ki zarurat hogi. Jaise, agar aap 1 glass doodh lo, toh 2 glass aam ki zarurat hogi. Lekin, agar aap 3 glass doodh lo, tab aapko 6 glass aam ki zarurat padegi.\u201d
\n\n\u201cRatio hamein yeh nahin batata hai ki kitne glass doodh istemaal karen. Lekin, agar hamein yeh pata ho, toh ham ratio istemaal karke dekh sakte hain ki hamein kitne glass aam chahiye honge.\u201d
\n\n\u201cOh, ab samajh aaya!\u201d James ne kaha. \u201cToh, jab aap yeh kahte hain ki ratio relative relationship batata hai, toh aapka matlab hai ki woh kitne number chahiye yeh nahin batata hai, haina? Bas itna ki ek cheez ka kitna chahiye, doosre cheez ke muqaable (relative)?\u201d
\n\n\u201cHan sahi hai!\u201d Uncle Berry ne kaha.
",
+ "data_format": "html",
+ "translation": {"translation" : "Uncle Berry ne kaha, \u201cHan, maine kaha tha ki ratio kayi cheezon ke raqam ki beech relative rishta batata hai. Ab main tumko bataunga ki \u2018relative \u2019 lafz ka kya matlab hai.\u201d
\n \n\n
\n\n\u201cIn glasses ko dekh rahe ho? Yahan, doodh se aam ka ratio 1:2 hai, kyunki aap jitna bhi doodh lo, uske dugne aam ki zarurat hogi. Jaise, agar aap 1 glass doodh lo, toh 2 glass aam ki zarurat hogi. Lekin, agar aap 3 glass doodh lo, tab aapko 6 glass aam ki zarurat padegi.\u201d
\n\n\u201cRatio hamein yeh nahin batata hai ki kitne glass doodh istemaal karen. Lekin, agar hamein yeh pata ho, toh ham ratio istemaal karke dekh sakte hain ki hamein kitne glass aam chahiye honge.\u201d
\n\n\u201cOh, ab samajh aaya!\u201d James ne kaha. \u201cToh, jab aap yeh kahte hain ki ratio relative relationship batata hai, toh aapka matlab hai ki woh kitne number chahiye yeh nahin batata hai, haina? Bas itna ki ek cheez ka kitna chahiye, doosre cheez ke muqaable (relative)?\u201d
\n\n\u201cHan sahi hai!\u201d Uncle Berry ne kaha.
"},
"needs_update": false
}
},
@@ -1631,17 +1631,17 @@
"customization_args": {
"choices": {
"value": [{
- "html": "The exact number of objects of each type.
",
- "content_id": "ca_choices_0"
+ "content_id": "ca_choices_0",
+ "html": "The exact number of objects of each type.
"
}, {
- "html": "The total number of objects we are trading.
",
- "content_id": "ca_choices_1"
+ "content_id": "ca_choices_1",
+ "html": "The total number of objects we are trading.
"
}, {
- "html": "The relative relationship between the amounts of different things.
",
- "content_id": "ca_choices_2"
+ "content_id": "ca_choices_2",
+ "html": "The relative relationship between the amounts of different things.
"
}, {
- "html": "I don\u2019t remember!
",
- "content_id": "ca_choices_3"
+ "content_id": "ca_choices_3",
+ "html": "I don\u2019t remember!
"
}]
}
},
@@ -1660,10 +1660,10 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
@@ -1679,10 +1679,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Definition of ratio",
@@ -1692,13 +1692,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -1763,29 +1763,29 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "Jab Uncle Berry ghar laute, James ne unko bataya ki usna kya kiya.
\n\n\u201cAh,\u201d Uncle Berry ne kaha. \u201cSmoothie banane ke liye tumko pise hue phal aur doodh ko sahi ratio pe milana hoga.\u201d Unhone James ko vyanjan ki tarqeebon ki bhari kitaab di. \u201cIs kitaab ko dekho. Kya tum samajh sakte ho ki doodh aur pise phal ka kya ratio hona chahiye?\u201d
\n\n\u201cAcha!\u201d James ne kaha. \u201cLekin doodh se pise phal ka ratio kaise likhenge ham? Yeh khilaune aur phal ki tarah nahin hai \u2014 \u2018ek doodh\u2019, ya \u2018do aam paste\u2019 ka koi matlab nahin hota hai na?\u201d
\n\n\u201cYeh acha sawaal hai, James!\u201d Uncle Berry ne kaha. \u201cKya tumhein yaad hai ki maine ratios ke baare mein pahle kya kaha tha? Koi ratio hamein kya batata hai?\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "Jab Uncle Berry ghar laute, James ne unko bataya ki usna kya kiya.
\n\n\u201cAh,\u201d Uncle Berry ne kaha. \u201cSmoothie banane ke liye tumko pise hue phal aur doodh ko sahi ratio pe milana hoga.\u201d Unhone James ko vyanjan ki tarqeebon ki bhari kitaab di. \u201cIs kitaab ko dekho. Kya tum samajh sakte ho ki doodh aur pise phal ka kya ratio hona chahiye?\u201d
\n\n\u201cAcha!\u201d James ne kaha. \u201cLekin doodh se pise phal ka ratio kaise likhenge ham? Yeh khilaune aur phal ki tarah nahin hai \u2014 \u2018ek doodh\u2019, ya \u2018do aam paste\u2019 ka koi matlab nahin hota hai na?\u201d
\n\n\u201cYeh acha sawaal hai, James!\u201d Uncle Berry ne kaha. \u201cKya tumhein yaad hai ki maine ratios ke baare mein pahle kya kaha tha? Koi ratio hamein kya batata hai?\u201d
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Yeh theek hai, lekin ek minute wapas socho ki ratio ka kya matlab hai. Ratios aayendah se istemaal aayenge, toh hamein inhein ache se samajhna chahiye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Yeh theek hai, lekin ek minute wapas socho ki ratio ka kya matlab hai. Ratios aayendah se istemaal aayenge, toh hamein inhein ache se samajhna chahiye.
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Bahut ache!
",
+ "data_format": "html",
+ "translation": {"translation" : "Bahut ache!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Yeh zaruri vishay hai, toh ek baar aur padh lo ki ratio ka kya matlab hota hai.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Yeh zaruri vishay hai, toh ek baar aur padh lo ki ratio ka kya matlab hota hai.
"},
"needs_update": false
}
}
@@ -1816,13 +1816,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -1846,8 +1846,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cLagta hai tumhein kuch phal khareedne padenge,\u201d Uncle Berry ne kaha.
\n\n\u201cLekin hamare paas paise nahin hain, Uncle Berry,\u201d James ne kaha.
\n\nPhir James ko ek khayaal aaya. \u201cHam apne doston se phal ke liye kuch khilaune badal sakte hain!\u201d usne kaha. \u201cUnko hamesha se hamare khilaunon se khelne ka shauq tha.\u201d
\n \n\n\u201cAcha idea hai!\u201d Uncle Berry ne kaha. \u201cLekin tum bina kisi niyam ke toh adla badli nahin karoge na? Pahle yeh decide kar lo ki khilaune to phal ko kis ratio pe badloge?\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cLagta hai tumhein kuch phal khareedne padenge,\u201d Uncle Berry ne kaha.
\n\n\u201cLekin hamare paas paise nahin hain, Uncle Berry,\u201d James ne kaha.
\n\nPhir James ko ek khayaal aaya. \u201cHam apne doston se phal ke liye kuch khilaune badal sakte hain!\u201d usne kaha. \u201cUnko hamesha se hamare khilaunon se khelne ka shauq tha.\u201d
\n \n\n\u201cAcha idea hai!\u201d Uncle Berry ne kaha. \u201cLekin tum bina kisi niyam ke toh adla badli nahin karoge na? Pahle yeh decide kar lo ki khilaune to phal ko kis ratio pe badloge?\u201d
"},
"needs_update": false
}
},
@@ -1879,13 +1879,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -1909,8 +1909,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "James phal ghar leke aa gaya. \u201cYeh phal dekhiye, Uncle Berry!\u201d usne chillaya, Uncle Berry ke kamre mein daudte hue. \u201cChalo smoothies banana shuroo karen!\u201d
\n\nBad-qismati se, Uncle Berry apne kamre mein nahin the. Woh kaam ke liye nikal chuke the.
\n\nToh, James ne smoothi khud banane ka faisla kiya. Usne rasoi mein jaakar blender dhoonda, aur har qism ke phal ko peesa. Iske baad, usne har pise hue phal ko ek ek bottle mein daal diya. Lekin usko yeh nahin pata tha ki har pise hue phal ko kaise milayen ki ek smoothi ban jaaye.
",
+ "data_format": "html",
+ "translation": {"translation" : "James phal ghar leke aa gaya. \u201cYeh phal dekhiye, Uncle Berry!\u201d usne chillaya, Uncle Berry ke kamre mein daudte hue. \u201cChalo smoothies banana shuroo karen!\u201d
\n\nBad-qismati se, Uncle Berry apne kamre mein nahin the. Woh kaam ke liye nikal chuke the.
\n\nToh, James ne smoothi khud banane ka faisla kiya. Usne rasoi mein jaakar blender dhoonda, aur har qism ke phal ko peesa. Iske baad, usne har pise hue phal ko ek ek bottle mein daal diya. Lekin usko yeh nahin pata tha ki har pise hue phal ko kaise milayen ki ek smoothi ban jaaye.
"},
"needs_update": false
}
},
@@ -1942,13 +1942,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -1972,8 +1972,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "James sofe pe baithke haath se choote smoothies ko yaad kar raha tha. Uske uncle, Uncle Berry, ne guzarte hue poocha \u201cTum itne udaas kyun ho James?\u201d
\n \n\n
\n\n\u201cHamein smoothie shop jaana hai lekin hamare paas itne paise hi nahin hain,\u201d James ne kaha. \u201cKya aap hamein kuch paise de sakte hain?\u201d
\n\n\u201cNahin,\u201d Uncle Berry ne kaha. \u201cLekin agar tum kuch phal le aao, toh ham dono milkar smoothie bana sakte hain!\u201d
\n\nJames kuch phal dhoondne ke liye daudte hue kitchen mein gaya. Lekin uske koi phal nahin mile.
",
+ "data_format": "html",
+ "translation": {"translation" : "James sofe pe baithke haath se choote smoothies ko yaad kar raha tha. Uske uncle, Uncle Berry, ne guzarte hue poocha \u201cTum itne udaas kyun ho James?\u201d
\n \n\n
\n\n\u201cHamein smoothie shop jaana hai lekin hamare paas itne paise hi nahin hain,\u201d James ne kaha. \u201cKya aap hamein kuch paise de sakte hain?\u201d
\n\n\u201cNahin,\u201d Uncle Berry ne kaha. \u201cLekin agar tum kuch phal le aao, toh ham dono milkar smoothie bana sakte hain!\u201d
\n\nJames kuch phal dhoondne ke liye daudte hue kitchen mein gaya. Lekin uske koi phal nahin mile.
"},
"needs_update": false
}
},
@@ -1993,7 +1993,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -2007,7 +2007,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "783afa39-0a6e-4eaa-9876-9a9833f1705c",
"normalizedStrSet": ["2 to 5"]
}
}
@@ -2015,7 +2015,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "3c2eb40b-911e-4023-8667-dc6a82b038c7",
"normalizedStrSet": ["2:5"]
}
}
@@ -2023,7 +2023,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "4819cb3d-38c6-46ab-86fa-fba0f61ef821",
"normalizedStrSet": ["\"2 to 5\""]
}
}
@@ -2036,16 +2036,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "cccb5b43-3764-4378-a7dd-de2151819128",
"normalizedStrSet": ["5 to 2"]
}
}
@@ -2058,16 +2058,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "6cf25801-0344-466d-86b1-3fd0b3cb4373",
"normalizedStrSet": ["2to5"]
}
}
@@ -2075,7 +2075,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "d649fc32-9e83-4ef6-a152-42f978ab9eee",
"normalizedStrSet": ["2 to5"]
}
}
@@ -2083,7 +2083,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "f7783e38-65cd-4c09-8612-a28f24a5ec37",
"normalizedStrSet": ["2to 5"]
}
}
@@ -2096,16 +2096,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "bba5b144-9374-40b4-8297-5aa3741d77ac",
"normalizedStrSet": ["X"]
}
}
@@ -2113,7 +2113,7 @@
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "b1e5f523-fe35-470d-a094-28dd1da45c8d",
"normalizedStrSet": ["Y"]
}
}
@@ -2126,16 +2126,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "7c26d744-cfef-4184-a42a-a8412133ee1f",
"normalizedStrSet": ["to"]
}
}
@@ -2148,10 +2148,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 1",
@@ -2161,13 +2161,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"feedback_2": {
@@ -2274,50 +2274,50 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cAb batao. Tum apne khelne ke kanche santron ke liye kaise badalne wale the?\u201d Uncle Berry ne poocha.
\n\n\u201cHam 2 kanche dekar 5 santre wapas lene wale the,\u201d James ne kaha.
\n \n\n
\n\nUncle Berry ne poocha, \u201cToh kanche se santre ka ratio kya hua?\u201d
\n\n(Apna jawaab \u201cX to Y\u201d ke form mein likho, jahan X aur Y ratio ke number hain.)
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cAb batao. Tum apne khelne ke kanche santron ke liye kaise badalne wale the?\u201d Uncle Berry ne poocha.
\n\n\u201cHam 2 kanche dekar 5 santre wapas lene wale the,\u201d James ne kaha.
\n \n\n
\n\nUncle Berry ne poocha, \u201cToh kanche se santre ka ratio kya hua?\u201d
\n\n(Apna jawaab \u201cX to Y\u201d ke form mein likho, jahan X aur Y ratio ke number hain.)
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi jawaab nahin hai. Numbers ki arrangement ka dhyaan rakhiye.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi jawaab nahin hai. Numbers ki arrangement ka dhyaan rakhiye.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Taqreeban sahi ho! Bas space ka dhyaan rakho. Pahle number baad ek space hona chahiye, aur \u201cto\u201d ke baad ek aur space.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Taqreeban sahi ho! Bas space ka dhyaan rakho. Pahle number baad ek space hona chahiye, aur \u201cto\u201d ke baad ek aur space.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Bahut Ache!
",
+ "data_format": "html",
+ "translation": {"translation" : "Bahut Ache!
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Kitne kanche aur kitne santrey badle jaa rahe hain, us pe dhyaan dijiye.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Kitne kanche aur kitne santrey badle jaa rahe hain, us pe dhyaan dijiye.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_5": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Aapko X aur Y mein sahi numbers likhne honge taaki ratio sahi nikle.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Aapko X aur Y mein sahi numbers likhne honge taaki ratio sahi nikle.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Aapke jawaab ko \u201cX to Y\u201d ki shakal mein hona chahiye, jahan X aur Y numbers hain. (Aapko quotations likhne ki zarurat nahin hai.)
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Aapke jawaab ko \u201cX to Y\u201d ki shakal mein hona chahiye, jahan X aur Y numbers hain. (Aapko quotations likhne ki zarurat nahin hai.)
"},
"needs_update": false
}
}
@@ -2348,13 +2348,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -2378,8 +2378,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "Pados mein ek mashhur smoothie shop khuli hai! James ko wahan jaane ka dil tha, lekin usne apne saare paise mithayi pe kharch kar diye the.
",
+ "data_format": "html",
+ "translation": {"translation" : "Pados mein ek mashhur smoothie shop khuli hai! James ko wahan jaane ka dil tha, lekin usne apne saare paise mithayi pe kharch kar diye the.
"},
"needs_update": false
}
},
@@ -2399,7 +2399,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -2413,7 +2413,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "9f99e701-3efe-4254-8fdc-3827d9ba498a",
"normalizedStrSet": ["50:20"]
}
}
@@ -2421,7 +2421,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "1bdb2184-acc7-4b05-a024-5a975a796067",
"normalizedStrSet": ["5:2"]
}
}
@@ -2434,16 +2434,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "724f7a4a-70aa-4a42-aab0-e1497424f465",
"normalizedStrSet": ["50 to 20"]
}
}
@@ -2451,7 +2451,7 @@
"rule_type": "CaseSensitiveEquals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "14186351-eee4-40fa-8f20-960306cfe465",
"normalizedStrSet": ["5 to 2"]
}
}
@@ -2464,16 +2464,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "63181669-ee99-438e-8088-7308bcb55e95",
"normalizedStrSet": ["20:50"]
}
}
@@ -2481,7 +2481,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "515833f7-3e0e-4eb4-a12e-629d95c43478",
"normalizedStrSet": ["2:5"]
}
}
@@ -2494,16 +2494,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "54af7fbe-67fb-43d7-946c-920648a0a414",
"normalizedStrSet": ["50: 20"]
}
}
@@ -2511,7 +2511,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "074938e1-b391-4d08-8d5f-ac14567cb05c",
"normalizedStrSet": ["50 :20"]
}
}
@@ -2519,7 +2519,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "f9d87552-db3c-40a1-a9b6-d4748dc3c51b",
"normalizedStrSet": ["50 : 20"]
}
}
@@ -2527,7 +2527,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "84be357c-d3f7-46c1-9b5a-527bbbb96058",
"normalizedStrSet": ["5: 2"]
}
}
@@ -2535,7 +2535,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "5101996c-3a50-4dbe-8ffc-dfb8f0ee564e",
"normalizedStrSet": ["5 :2"]
}
}
@@ -2543,7 +2543,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "d3299662-c092-44cb-95b3-b07dc0cf50fa",
"normalizedStrSet": ["5 : 2"]
}
}
@@ -2556,10 +2556,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 3",
@@ -2569,13 +2569,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"feedback_2": {
@@ -2668,43 +2668,43 @@
"translations_mapping": {
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Ratio sahi hai! Lekin, ab se, apna jawaab colon ke saath hi likhna. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Ratio sahi hai! Lekin, ab se, apna jawaab colon ke saath hi likhna. Phir se koshish kariye.
"},
"needs_update": false
}
},
"content": {
"hi": {
- "data_format": "",
- "translation": "James ne apne baqi adla badli ratios nikalkar, apne doston ke saath khilaune badalna shuroo kar diya.
\n\n\u201cHam tumhein 50 kanche denge, aur tum hamko 20 strawberry dena,\u201d James ne kaha apne ek dost se.
\n\nIs maamle mein kanche se stawberry ka kya ratio hai?
",
+ "data_format": "html",
+ "translation": {"translation" : "James ne apne baqi adla badli ratios nikalkar, apne doston ke saath khilaune badalna shuroo kar diya.
\n\n\u201cHam tumhein 50 kanche denge, aur tum hamko 20 strawberry dena,\u201d James ne kaha apne ek dost se.
\n\nIs maamle mein kanche se stawberry ka kya ratio hai?
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin, tumne ghalta order likha hai. Ratios mein, number ka arrangement sahi hona chahiye! Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, tumne ghalta order likha hai. Ratios mein, number ka arrangement sahi hona chahiye! Phir se koshish kariye.
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Sahi!
\n\nHam log ratio ko 50:20 likh sakte hain ya 5:3, kyunki hamein sirf relative number ki fiqr hai. Dono jawaab sahi hain.
\n\n5:2 sahi hai kyunki har baar James 5 kanche deta hai, usko 2 strawberries wapas milte hain (aur woh yeh kaam 10 baar sakta hai taaki woh 50 kanche dekar 20 strawberries wapas le).
",
+ "data_format": "html",
+ "translation": {"translation" : "Sahi!
\n\nHam log ratio ko 50:20 likh sakte hain ya 5:3, kyunki hamein sirf relative number ki fiqr hai. Dono jawaab sahi hain.
\n\n5:2 sahi hai kyunki har baar James 5 kanche deta hai, usko 2 strawberries wapas milte hain (aur woh yeh kaam 10 baar sakta hai taaki woh 50 kanche dekar 20 strawberries wapas le).
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": "Yeh sahi ratio hai, lekin ek choti cheez: jab ratio type karo, tab tumhein colon ke aaspaas space likhne ki zarurat nahin hai. Kya tum apna jawaab phir se likh sakte ho, bina kisi space ke?
",
+ "data_format": "html",
+ "translation": {"translation" : "Yeh sahi ratio hai, lekin ek choti cheez: jab ratio type karo, tab tumhein colon ke aaspaas space likhne ki zarurat nahin hai. Kya tum apna jawaab phir se likh sakte ho, bina kisi space ke?
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Sawaal ke number dhyaan se padho.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Sawaal ke number dhyaan se padho.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
}
@@ -2723,7 +2723,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -2737,7 +2737,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "c2b0772b-c38a-44c5-a14a-fe680a65a9e8",
"normalizedStrSet": ["3 to 1"]
}
}
@@ -2745,7 +2745,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "bac00fc3-4953-4049-9dbe-52b04b2e85cf",
"normalizedStrSet": ["3:1"]
}
}
@@ -2753,7 +2753,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "29006547-391f-474a-ac64-dbb2ab0de333",
"normalizedStrSet": ["\"3 to 1\""]
}
}
@@ -2766,16 +2766,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "3bc9759f-b033-4dbf-a550-4670da8ecf3b",
"normalizedStrSet": ["1 to 3"]
}
}
@@ -2788,16 +2788,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "992b6fb4-4550-4278-a083-8e3f8580410e",
"normalizedStrSet": ["3to1"]
}
}
@@ -2805,7 +2805,7 @@
"rule_type": "CaseSensitiveEquals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "c78bf24e-b510-4bac-a107-28a03dfa1c22",
"normalizedStrSet": ["3 to1"]
}
}
@@ -2813,7 +2813,7 @@
"rule_type": "CaseSensitiveEquals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "338cb82a-5e6f-460a-8fa0-5a2510c1a31e",
"normalizedStrSet": ["3to 1"]
}
}
@@ -2826,16 +2826,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "7e96afac-4b8d-4a10-94bb-a146c5078c94",
"normalizedStrSet": ["X"]
}
}
@@ -2843,7 +2843,7 @@
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "1c480baf-c5a9-43e3-a746-27febfe4e818",
"normalizedStrSet": ["Y"]
}
}
@@ -2856,16 +2856,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "930b1d9c-2b0e-4c89-8247-a0665db536af",
"normalizedStrSet": ["to"]
}
}
@@ -2878,10 +2878,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 2",
@@ -2891,13 +2891,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"feedback_2": {
@@ -3004,50 +3004,50 @@
"translations_mapping": {
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi jawaab nahin hai. Numbers ki arrangement ka dhyaan rakhiye.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi jawaab nahin hai. Numbers ki arrangement ka dhyaan rakhiye.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cBahut ache, James!\u201d Uncle Berry ne kaha. \u201cPahle 2 likha jaata hai aur phir 5, kyunki ratio kanche to santre ka hai.Yeh numbers ka arrangement zaruri hai. Unko hamesha isi tarah se likhna chahiye.\u201d
\n \n\n
\n\n\u201cAb batao, tum kanche se seb kaise badloge?\u201d unhone poocha.
\n \n\n
\n\nJames ne kaha, \u201cHam 3 kanchon se 1 seb badlenge. Toh, kanche to seb ka ratio hoga \u2026\u201d
\n\nKya tum kanche to seb ka ratio likh sakte ho?
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cBahut ache, James!\u201d Uncle Berry ne kaha. \u201cPahle 2 likha jaata hai aur phir 5, kyunki ratio kanche to santre ka hai.Yeh numbers ka arrangement zaruri hai. Unko hamesha isi tarah se likhna chahiye.\u201d
\n \n\n
\n\n\u201cAb batao, tum kanche se seb kaise badloge?\u201d unhone poocha.
\n \n\n
\n\nJames ne kaha, \u201cHam 3 kanchon se 1 seb badlenge. Toh, kanche to seb ka ratio hoga \u2026\u201d
\n\nKya tum kanche to seb ka ratio likh sakte ho?
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Number sahi likhen hain! Bas space par dhyan do. \u201c3\u201d ke baad ek space hone chahiye, aur \u201cto\u201d ke baad ek space.
\n\nApna jawaab phir se likhne ki koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Number sahi likhen hain! Bas space par dhyan do. \u201c3\u201d ke baad ek space hone chahiye, aur \u201cto\u201d ke baad ek space.
\n\nApna jawaab phir se likhne ki koshish kariye.
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Sahi kaha! Bahut acha kar rahe ho!
",
+ "data_format": "html",
+ "translation": {"translation" : "Sahi kaha! Bahut acha kar rahe ho!
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Kitne kanche aur kitne seb badle jaa rahe hain, us pe dhyaan dijiye.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Kitne kanche aur kitne seb badle jaa rahe hain, us pe dhyaan dijiye.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_5": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Aapko X aur Y mein sahi numbers likhne honge taaki ratio sahi nikle.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Aapko X aur Y mein sahi numbers likhne honge taaki ratio sahi nikle.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Aapke jawaab ko \u201cX to Y\u201d ki shakal mein hona chahiye, jahan X aur Y numbers hain.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Aapke jawaab ko \u201cX to Y\u201d ki shakal mein hona chahiye, jahan X aur Y numbers hain.
"},
"needs_update": false
}
}
@@ -3066,7 +3066,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -3080,7 +3080,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "d4f1cd28-f796-4b32-9080-cf2c283039bf",
"normalizedStrSet": ["2:1"]
}
}
@@ -3088,7 +3088,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "4161eeb4-38ae-43ca-8d06-3f62f6318c75",
"normalizedStrSet": ["1:1/2"]
}
}
@@ -3096,7 +3096,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "72c48438-98eb-4cd5-b6dc-dca3034c98d5",
"normalizedStrSet": ["1:0.5"]
}
}
@@ -3109,16 +3109,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "StartsWith",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "30bfedac-7ea5-44b9-95ef-3aeb08ba5d07",
"normalizedStrSet": ["1"]
}
}
@@ -3131,16 +3131,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "1bfd83da-8e96-488a-84c6-985d1367af6e",
"normalizedStrSet": ["2: 1"]
}
}
@@ -3153,16 +3153,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "44624b4f-5531-4e6f-8a91-539354be9596",
"normalizedStrSet": ["2:"]
}
}
@@ -3175,10 +3175,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 5",
@@ -3188,13 +3188,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"feedback_2": {
@@ -3287,43 +3287,43 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "James ne page palta, aur usko kele ke smoothie ki recipe dikhi. Mazedaar!
\n \n\n
\n\n\u201cMain yeh bana sakta hun,\u2019 Usne kaha. \u201cHamein doodh aur kele ko ____________ ratio mein milana hoga.\u201d
\n\nKya aap James ke jumle ko poora kar sakte hain? Doodh se kele ka kya ratio hai?
",
+ "data_format": "html",
+ "translation": {"translation" : "James ne page palta, aur usko kele ke smoothie ki recipe dikhi. Mazedaar!
\n \n\n
\n\n\u201cMain yeh bana sakta hun,\u2019 Usne kaha. \u201cHamein doodh aur kele ko ____________ ratio mein milana hoga.\u201d
\n\nKya aap James ke jumle ko poora kar sakte hain? Doodh se kele ka kya ratio hai?
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Ratio ka baaya side hamesha 1 nahin hota. Phir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Ratio ka baaya side hamesha 1 nahin hota. Phir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "
\n\nYeh achi shuruaat hai, lekin tumhare paas kela sahi raqam mein nahin hai. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "
\n\nYeh achi shuruaat hai, lekin tumhare paas kela sahi raqam mein nahin hai. Phir se koshish kariye.
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Sahi kaha!
",
+ "data_format": "html",
+ "translation": {"translation" : "Sahi kaha!
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": "
\n\nRatio ko bina space ke likha karo. Phir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "
\n\nRatio ko bina space ke likha karo. Phir se koshish kariye!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai! Phir se koshish karo. (Aur yaad rakhna ki ratio ko spaces ke bina likhna chahiye.)
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai! Phir se koshish karo. (Aur yaad rakhna ki ratio ko spaces ke bina likhna chahiye.)
"},
"needs_update": false
}
}
@@ -3342,7 +3342,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -3356,7 +3356,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "9188484d-a59e-4932-9936-6d54e287f152",
"normalizedStrSet": ["1:4"]
}
}
@@ -3364,7 +3364,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "1abb261a-14b8-4d67-850e-a1f173e0c824",
"normalizedStrSet": ["2:8"]
}
}
@@ -3377,16 +3377,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Contains",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "9e160745-9991-4651-b1f3-fc46922b1d92",
"normalizedStrSet": ["to"]
}
}
@@ -3399,10 +3399,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 4",
@@ -3412,8 +3412,8 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [{
"hint_content": {
@@ -3423,7 +3423,7 @@
}],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -3502,36 +3502,36 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cBahut ache! Ab, yeh ek aur sawaal hai.\u201d
\n\n\u201cSocho ki hamein doodh aur strawberry mix karna neeche ke tasveer jaise. Is mein pichli tasveer se bade glasses hain. Doodh se strawberry ka kya ratio hai?\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cBahut ache! Ab, yeh ek aur sawaal hai.\u201d
\n\n\u201cSocho ki hamein doodh aur strawberry mix karna neeche ke tasveer jaise. Is mein pichli tasveer se bade glasses hain. Doodh se strawberry ka kya ratio hai?\u201d
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Apna jawaab X:Y ki shakl mein likhiye, jahan X aur Y numbers hain. Phir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Apna jawaab X:Y ki shakl mein likhiye, jahan X aur Y numbers hain. Phir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Sahi! Ratio abhi bhi 1:4 hai. Halaanki glasses bade hain, hamare paas abhi bhi doodh ke chaar gunaah strawberry hoga.
",
+ "data_format": "html",
+ "translation": {"translation" : "Sahi! Ratio abhi bhi 1:4 hai. Halaanki glasses bade hain, hamare paas abhi bhi doodh ke chaar gunaah strawberry hoga.
"},
"needs_update": false
}
},
"hint_1": {
"hi": {
- "data_format": "",
- "translation": "Yaad rakho, doodh aur strawberry ki exact raqam zaruri nahin hai. Unki ek doosre ke relative raqam zaruri hai.
",
+ "data_format": "html",
+ "translation": {"translation" : "Yaad rakho, doodh aur strawberry ki exact raqam zaruri nahin hai. Unki ek doosre ke relative raqam zaruri hai.
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Socho ki baany haath pe doodh ka glass 1 unit hai, toh aapko ratio 1:Y ki shakal mein likhnahoga (jahan aapko samajhna hoga ki Y kya hai).
\n\nKya tum tasveer ko dekhke Y kya hai nikal sakte ho? Phir, poora ratio likho.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Socho ki baany haath pe doodh ka glass 1 unit hai, toh aapko ratio 1:Y ki shakal mein likhnahoga (jahan aapko samajhna hoga ki Y kya hai).
\n\nKya tum tasveer ko dekhke Y kya hai nikal sakte ho? Phir, poora ratio likho.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
}
@@ -3550,24 +3550,24 @@
"id": "ItemSelectionInput",
"customization_args": {
"maxAllowableSelectionCount": {
- "value": 4.0
+ "value": 4
},
"minAllowableSelectionCount": {
- "value": 1.0
+ "value": 1
},
"choices": {
"value": [{
- "html": " \n
",
- "content_id": "ca_choices_0"
+ "content_id": "ca_choices_0",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_1"
+ "content_id": "ca_choices_1",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_2"
+ "content_id": "ca_choices_2",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_3"
+ "content_id": "ca_choices_3",
+ "html": " \n
"
}]
}
},
@@ -3586,10 +3586,10 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
@@ -3610,10 +3610,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "ContainsAtLeastOneOf",
@@ -3629,10 +3629,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
@@ -3648,10 +3648,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 7",
@@ -3661,8 +3661,8 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [{
"hint_content": {
@@ -3677,7 +3677,7 @@
}],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -3798,57 +3798,57 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cSahi!\u201d kaha Uncle Berry ne. \u201cJames, mere paas ek idea aaya. Tumhein chocolate smoothie bhi banani hai kya? Chocolate smoothies phal ke smoothies jitne ache lagte hain!\u201d
\n\n\u201cAur nahin toh!\u201d James ne jawaab diya.
\n\n \n\u201cTheek hai,\u201d Uncle Berry ne kaha. \u201cYeh recipe white chocolate aur dark chocolate ko 1:3 ki ratio mein pees rahi hai. Dekho agar tum woh sab tasveeren chun sakte ho jin mein yeh ratio sahi se dikhaya gaya ho.\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cSahi!\u201d kaha Uncle Berry ne. \u201cJames, mere paas ek idea aaya. Tumhein chocolate smoothie bhi banani hai kya? Chocolate smoothies phal ke smoothies jitne ache lagte hain!\u201d
\n\n\u201cAur nahin toh!\u201d James ne jawaab diya.
\n\n \n\u201cTheek hai,\u201d Uncle Berry ne kaha. \u201cYeh recipe white chocolate aur dark chocolate ko 1:3 ki ratio mein pees rahi hai. Dekho agar tum woh sab tasveeren chun sakte ho jin mein yeh ratio sahi se dikhaya gaya ho.\u201d
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Yaad rakho, ratio kisi samaan ki exact raqam nahin dikhata hai. Toh, yeh zaruri nahin hai ki white chocolate ka sirf 1 tukda hoga.
\n\nRatio hamesha relative rishta dikhata hai. Toh, is sawaal mein, yeh dhyaan rakho ki white chocolate ke har 1 tukde ke liye , dark chocolate ke 3 tukde hone chahiyen.
\n\nDekho agar tum yeh jaankari se jawaab nikal sakte ho!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Yaad rakho, ratio kisi samaan ki exact raqam nahin dikhata hai. Toh, yeh zaruri nahin hai ki white chocolate ka sirf 1 tukda hoga.
\n\nRatio hamesha relative rishta dikhata hai. Toh, is sawaal mein, yeh dhyaan rakho ki white chocolate ke har 1 tukde ke liye , dark chocolate ke 3 tukde hone chahiyen.
\n\nDekho agar tum yeh jaankari se jawaab nikal sakte ho!
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Tumne ek jawaab toh sahi diya hai! Lekin phir se dekho ki tumne sab sahi jawaab chune ki nahin.
",
+ "data_format": "html",
+ "translation": {"translation" : "Tumne ek jawaab toh sahi diya hai! Lekin phir se dekho ki tumne sab sahi jawaab chune ki nahin.
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Tumhara ek jawaab 1:2 ka ratio dikha raha hai. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Tumhara ek jawaab 1:2 ka ratio dikha raha hai. Phir se koshish kariye.
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Bahut ache!
",
+ "data_format": "html",
+ "translation": {"translation" : "Bahut ache!
"},
"needs_update": false
}
},
"hint_1": {
"hi": {
- "data_format": "",
- "translation": "Ek se zyadah sahi jawaab hain.
",
+ "data_format": "html",
+ "translation": {"translation" : "Ek se zyadah sahi jawaab hain.
"},
"needs_update": false
}
},
"hint_2": {
"hi": {
- "data_format": "",
- "translation": "\u201cRelatively\u201d socha karo. Har 1 unit white chocolate ke liye, 3 unit dark chocolate hone chahiyen.
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cRelatively\u201d socha karo. Har 1 unit white chocolate ke liye, 3 unit dark chocolate hone chahiyen.
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai.
\n\nPhir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai.
\n\nPhir se koshish kariye.
"},
"needs_update": false
}
}
@@ -3868,20 +3868,20 @@
"customization_args": {
"choices": {
"value": [{
- "html": " \n
",
- "content_id": "ca_choices_0"
+ "content_id": "ca_choices_0",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_1"
+ "content_id": "ca_choices_1",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_2"
+ "content_id": "ca_choices_2",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_3"
+ "content_id": "ca_choices_3",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_4"
+ "content_id": "ca_choices_4",
+ "html": " \n
"
}]
}
},
@@ -3900,10 +3900,10 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
@@ -3919,10 +3919,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
@@ -3938,10 +3938,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
@@ -3957,10 +3957,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 6",
@@ -3970,13 +3970,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -4069,43 +4069,43 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cBahut ache!\u201d Uncle Berry ne kaha. Unhone rasoi ki table ko dekha aur unko ehsaas hua ki James ke paas ek jug santre ka juice tha jo istemaal nahin hua tha.
\n\n\u201cAb, James,\u201d unhone kaha, \u201cMujhe santre ke smoothie ki ek achi recipe pata hai. Uske liye santre aur doodh ko 3:2 ratio mein milane ki zarurat hai. Kya tum mujhe bata sakte ho ki aage ki tasveeron mein se kaun si tasveer santre se doodh ka 3:2 ratio dikha rahi hai?\u201d
\n\n(Dhyaan deejiye: Santre ka puree orange hai, aur doodh white.)
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cBahut ache!\u201d Uncle Berry ne kaha. Unhone rasoi ki table ko dekha aur unko ehsaas hua ki James ke paas ek jug santre ka juice tha jo istemaal nahin hua tha.
\n\n\u201cAb, James,\u201d unhone kaha, \u201cMujhe santre ke smoothie ki ek achi recipe pata hai. Uske liye santre aur doodh ko 3:2 ratio mein milane ki zarurat hai. Kya tum mujhe bata sakte ho ki aage ki tasveeron mein se kaun si tasveer santre se doodh ka 3:2 ratio dikha rahi hai?\u201d
\n\n(Dhyaan deejiye: Santre ka puree orange hai, aur doodh white.)
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh santre se doodh ka 3:1 ratio dikha raha hai. Tumhein 3:2 ratio ki zarurat hai.
\n\nShayad tumhein ratio ke matlab ko phir se dekhne ki zarurat hai. Chalo wapas chalke Uncle Berry se phir se seekhen!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh santre se doodh ka 3:1 ratio dikha raha hai. Tumhein 3:2 ratio ki zarurat hai.
\n\nShayad tumhein ratio ke matlab ko phir se dekhne ki zarurat hai. Chalo wapas chalke Uncle Berry se phir se seekhen!
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin yeh dikhata hai ki santre se doodh ka ratio 1:2 hai. Lekin tumko woh tasveer chunna chahiye jo 3:2 ratio dikha raha hai.
\n\nShayad tumhein ratio ke matlab ko phir se dekhne ki zarurat hai. Chalo wapas chalke Uncle Berry se phir se seekhen!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin yeh dikhata hai ki santre se doodh ka ratio 1:2 hai. Lekin tumko woh tasveer chunna chahiye jo 3:2 ratio dikha raha hai.
\n\nShayad tumhein ratio ke matlab ko phir se dekhne ki zarurat hai. Chalo wapas chalke Uncle Berry se phir se seekhen!
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Sahi kiya!
",
+ "data_format": "html",
+ "translation": {"translation" : "Sahi kiya!
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": "Nahin yeh dikhata hai ki santre se doodh ka ratio 1:3 hai. Lekin sawaal ka yeh sahi jawaab nahin hai.
\n\nShayad tumhein ratio ke matlab ko phir se dekhne ki zarurat hai. Chalo wapas chalke Uncle Berry se phir se seekhen! Apna waqt lekar samajhne ki koshish karo ki woh James se kya kah rahe hain.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin yeh dikhata hai ki santre se doodh ka ratio 1:3 hai. Lekin sawaal ka yeh sahi jawaab nahin hai.
\n\nShayad tumhein ratio ke matlab ko phir se dekhne ki zarurat hai. Chalo wapas chalke Uncle Berry se phir se seekhen! Apna waqt lekar samajhne ki koshish karo ki woh James se kya kah rahe hain.
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Phir se koshish kariye, order pe dhyaan dijiye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Phir se koshish kariye, order pe dhyaan dijiye!
"},
"needs_update": false
}
}
@@ -4124,27 +4124,27 @@
"id": "ItemSelectionInput",
"customization_args": {
"maxAllowableSelectionCount": {
- "value": 5.0
+ "value": 5
},
"minAllowableSelectionCount": {
- "value": 1.0
+ "value": 1
},
"choices": {
"value": [{
- "html": "For every glass of banana puree, we need 2 glasses of milk.
",
- "content_id": "ca_choices_0"
+ "content_id": "ca_choices_0",
+ "html": "For every glass of banana puree, we need 2 glasses of milk.
"
}, {
- "html": "For any amount of banana puree, we need twice (= 2 times) that amount of milk.
",
- "content_id": "ca_choices_1"
+ "content_id": "ca_choices_1",
+ "html": "For any amount of banana puree, we need twice (= 2 times) that amount of milk.
"
}, {
- "html": "We can make this smoothie by mixing 2 cups of milk and 4 cups of banana puree.
",
- "content_id": "ca_choices_2"
+ "content_id": "ca_choices_2",
+ "html": "We can make this smoothie by mixing 2 cups of milk and 4 cups of banana puree.
"
}, {
- "html": "The smoothie must contain exactly 1 glass of banana puree and 2 glasses of milk.
",
- "content_id": "ca_choices_3"
+ "content_id": "ca_choices_3",
+ "html": "The smoothie must contain exactly 1 glass of banana puree and 2 glasses of milk.
"
}, {
- "html": "We can make this smoothie using 3 glasses of banana puree and 6 glasses of milk.
",
- "content_id": "ca_choices_4"
+ "content_id": "ca_choices_4",
+ "html": "We can make this smoothie using 3 glasses of banana puree and 6 glasses of milk.
"
}]
}
},
@@ -4163,10 +4163,10 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "ContainsAtLeastOneOf",
@@ -4182,10 +4182,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "ContainsAtLeastOneOf",
@@ -4201,10 +4201,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "DoesNotContainAtLeastOneOf",
@@ -4220,10 +4220,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "DoesNotContainAtLeastOneOf",
@@ -4239,10 +4239,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 9",
@@ -4252,13 +4252,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -4365,50 +4365,50 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cMujhe lagta hai ki mujhe ratios ab samajh aa rahe hain!\u201d James ne khushi se kaha.
\n\n\u201cHhhhmm, shayad tumhein aa rahe hain!\u201d Uncle Berry ne kaha. \u201cKya ham bas ek aur sawaal padhen, bas hamare itmenaan ke liye?\u201d
\n\n\u201cHan!\u201d kaha James ne.
\n\n \n\u201cAcha,\u201d kaha Uncle Berry ne. \u201cKya tumhein yaad hai ki bas abhi ham kele ke smoothies ke baat kar rahe the? Is smoothie mein kele se doodh ka ratio 1:2 tha. In jumlon mein se kaun se is haal ko sahi se bayaan kar raha hai? Sab sahi wale chuno.\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cMujhe lagta hai ki mujhe ratios ab samajh aa rahe hain!\u201d James ne khushi se kaha.
\n\n\u201cHhhhmm, shayad tumhein aa rahe hain!\u201d Uncle Berry ne kaha. \u201cKya ham bas ek aur sawaal padhen, bas hamare itmenaan ke liye?\u201d
\n\n\u201cHan!\u201d kaha James ne.
\n\n \n\u201cAcha,\u201d kaha Uncle Berry ne. \u201cKya tumhein yaad hai ki bas abhi ham kele ke smoothies ke baat kar rahe the? Is smoothie mein kele se doodh ka ratio 1:2 tha. In jumlon mein se kaun se is haal ko sahi se bayaan kar raha hai? Sab sahi wale chuno.\u201d
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Yeh sahi shuruaat hai, lekin tumhein sirf kuch kuch sahi jawaab mile hain. Saare sahi jawaab chuno. Phir se koshish karo!
",
+ "data_format": "html",
+ "translation": {"translation" : "Yeh sahi shuruaat hai, lekin tumhein sirf kuch kuch sahi jawaab mile hain. Saare sahi jawaab chuno. Phir se koshish karo!
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Yaad rakho, ratios hamesha relative rishte dikhate hain, exact raqam nahin. Uncle Berry ki baatein phir se padhte hain.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Yaad rakho, ratios hamesha relative rishte dikhate hain, exact raqam nahin. Uncle Berry ki baatein phir se padhte hain.
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Han, yeh sahi hai! Tumne sahi kiya!
",
+ "data_format": "html",
+ "translation": {"translation" : "Han, yeh sahi hai! Tumne sahi kiya!
"},
"needs_update": false
}
},
"feedback_4": {
"hi": {
- "data_format": "",
- "translation": "Oops, dhyaan se! Ratio mein cheezon ka order zaruri hai, lekin diye hue chunaav mein se ek aisa hai jis mein cheezen ghalat order mein likhi hain. Dhyaan rakho ki tum woh wala na chuno.
\n\nPhir se koshish karo.
",
+ "data_format": "html",
+ "translation": {"translation" : "Oops, dhyaan se! Ratio mein cheezon ka order zaruri hai, lekin diye hue chunaav mein se ek aisa hai jis mein cheezen ghalat order mein likhi hain. Dhyaan rakho ki tum woh wala na chuno.
\n\nPhir se koshish karo.
"},
"needs_update": false
}
},
"feedback_5": {
"hi": {
- "data_format": "",
- "translation": "Yeh achi shuruaat hai, lekin dhyaan rakho ki sab sahi jawaab chuno. Yaad rakho, ratios relative rishte dikhaate hain, toh aap kitne bhi kele istemaal kar sakte hain, us hisaab se sahi raqam doodh bhi le lo.
",
+ "data_format": "html",
+ "translation": {"translation" : "Yeh achi shuruaat hai, lekin dhyaan rakho ki sab sahi jawaab chuno. Yaad rakho, ratios relative rishte dikhaate hain, toh aap kitne bhi kele istemaal kar sakte hain, us hisaab se sahi raqam doodh bhi le lo.
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hain. Phir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hain. Phir se koshish kariye!
"},
"needs_update": false
}
}
@@ -4427,27 +4427,27 @@
"id": "ItemSelectionInput",
"customization_args": {
"maxAllowableSelectionCount": {
- "value": 5.0
+ "value": 5
},
"minAllowableSelectionCount": {
- "value": 1.0
+ "value": 1
},
"choices": {
"value": [{
- "html": " \n
",
- "content_id": "ca_choices_0"
+ "content_id": "ca_choices_0",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_1"
+ "content_id": "ca_choices_1",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_2"
+ "content_id": "ca_choices_2",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_3"
+ "content_id": "ca_choices_3",
+ "html": " \n
"
}, {
- "html": " \n
",
- "content_id": "ca_choices_4"
+ "content_id": "ca_choices_4",
+ "html": " \n
"
}]
}
},
@@ -4466,10 +4466,10 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "ContainsAtLeastOneOf",
@@ -4485,10 +4485,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "ContainsAtLeastOneOf",
@@ -4504,10 +4504,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 8",
@@ -4517,13 +4517,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -4602,36 +4602,36 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cAcha, tum is pe ache bante jaa rahe ho!\u201d Uncle Berry ne kaha.
\n\n\u201cYeh wala thoda aur mushkil hai. Woh saari tasveeren chuno jo sahi se 1:6 ka ratio dikha rahi hain.\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cAcha, tum is pe ache bante jaa rahe ho!\u201d Uncle Berry ne kaha.
\n\n\u201cYeh wala thoda aur mushkil hai. Woh saari tasveeren chuno jo sahi se 1:6 ka ratio dikha rahi hain.\u201d
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Yeh sahi hai, acha kiya! Tum is pe bahut ache ho!
",
+ "data_format": "html",
+ "translation": {"translation" : "Yeh sahi hai, acha kiya! Tum is pe bahut ache ho!
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Hhhhm, tumne kuch sahi chune hain, lekin aur bhi sahi hain. Dhyaan rakho ki tum sab sahi jawaab chun rahe ho!
",
+ "data_format": "html",
+ "translation": {"translation" : "Hhhhm, tumne kuch sahi chune hain, lekin aur bhi sahi hain. Dhyaan rakho ki tum sab sahi jawaab chun rahe ho!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Phir se koshish karo.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Phir se koshish karo.
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin yeh, sahi nahin hai. Tumne kuch aisi tasveeren chuni hain jo aise ratio dikha rahi hain jo ki 1:6 nahin hain.
\n\nYaad rakho, ratios relative rishte dikhate hain. Toh, dhyan rakho ki kisi cheez A ke har 1 unit ke liye, tum doosri cheez B ke 6 unit rakho.
\n\nPhir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin yeh, sahi nahin hai. Tumne kuch aisi tasveeren chuni hain jo aise ratio dikha rahi hain jo ki 1:6 nahin hain.
\n\nYaad rakho, ratios relative rishte dikhate hain. Toh, dhyan rakho ki kisi cheez A ke har 1 unit ke liye, tum doosri cheez B ke 6 unit rakho.
\n\nPhir se koshish kariye!
"},
"needs_update": false
}
}
@@ -4662,13 +4662,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -4692,8 +4692,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "James aur Uncle Berry ne apne din ka aakhri smoothie banaya. Woh bahut swaadisht tha! Aur ab, kyunki James ko ratios ke baare mein pata tha, woh apni tarqeeben apne doston ko bhi bata sakta tha. Glass saaf karte hue James ne faisla kiya ki woh kal khud se aur smoothies banayega.
\n\nJames ne kuch zaruri cheezen bhi yaad rakhin. Tumhein yeh likh leni chahiyen taaki tumhein bhi yaad rahen!
\n\n\n\t\n\tRatios kayi cheezon ke raqamon ke beech ke relative rishte ke baare mein batata hai.
\n\t \n\t\n\tWoh \u2018to\u2019, ya ek colon (\u2018:\u2019) ki zariye likhe jaa sakte hain. E.g. \u20182 to 3\u2019 ya \u20182:3\u2019.
\n\t \n\t\n\tJab ratio likho, toh tumhein sirf un cheezon pe dhyaan dena chahiye jinke baare mein poocha gaya ho. Baqi sab ke baare mein bhool sakte ho!
\n\t ",
+ "data_format": "html",
+ "translation": {"translation" : "James aur Uncle Berry ne apne din ka aakhri smoothie banaya. Woh bahut swaadisht tha! Aur ab, kyunki James ko ratios ke baare mein pata tha, woh apni tarqeeben apne doston ko bhi bata sakta tha. Glass saaf karte hue James ne faisla kiya ki woh kal khud se aur smoothies banayega.
\n\nJames ne kuch zaruri cheezen bhi yaad rakhin. Tumhein yeh likh leni chahiyen taaki tumhein bhi yaad rahen!
\n\n\n\t\n\tRatios kayi cheezon ke raqamon ke beech ke relative rishte ke baare mein batata hai.
\n\t \n\t\n\tWoh \u2018to\u2019, ya ek colon (\u2018:\u2019) ki zariye likhe jaa sakte hain. E.g. \u20182 to 3\u2019 ya \u20182:3\u2019.
\n\t \n\t\n\tJab ratio likho, toh tumhein sirf un cheezon pe dhyaan dena chahiye jinke baare mein poocha gaya ho. Baqi sab ke baare mein bhool sakte ho!
\n\t "},
"needs_update": false
}
},
@@ -4725,13 +4725,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -4755,8 +4755,8 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cYeh ratio kya hota hai?\u201d James ne hairaan hokar poocha.
\n\n\u201cOh,\u201d Uncle Berry ne kaha. \u201cRatios bahut faydah-mand hote hai. Ratio kuch cheezon ki ginti , kuch aur cheezon ke muqaable mein batati hai. Jaisi ki, tum apne khilaune phal ke liye kaise badalne wale the?\u201d
\n\n\u201cHam 1 khilauna dekar 2 aam wapas lene wale the,\u201d James ne kaha.
\n\n\u201cAcha,\u201d Uncle Berry ne kaha. \u201cToh, tum aisa kah sakte ho ki tum khilaune aur aam 1 to 2 ki ratio mein badal rahe ho. Yaani khilaune to aam ka ratio 1 to 2 hai. \u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cYeh ratio kya hota hai?\u201d James ne hairaan hokar poocha.
\n\n\u201cOh,\u201d Uncle Berry ne kaha. \u201cRatios bahut faydah-mand hote hai. Ratio kuch cheezon ki ginti , kuch aur cheezon ke muqaable mein batati hai. Jaisi ki, tum apne khilaune phal ke liye kaise badalne wale the?\u201d
\n\n\u201cHam 1 khilauna dekar 2 aam wapas lene wale the,\u201d James ne kaha.
\n\n\u201cAcha,\u201d Uncle Berry ne kaha. \u201cToh, tum aisa kah sakte ho ki tum khilaune aur aam 1 to 2 ki ratio mein badal rahe ho. Yaani khilaune to aam ka ratio 1 to 2 hai. \u201d
"},
"needs_update": false
}
},
@@ -4776,7 +4776,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -4790,7 +4790,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "2218f74f-cb3d-4b3d-8f7f-48901af0f33d",
"normalizedStrSet": ["1:4"]
}
}
@@ -4803,16 +4803,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "f498979c-a3cf-4a31-889a-b86aed44cfb5",
"normalizedStrSet": ["1 to 4"]
}
}
@@ -4825,16 +4825,16 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "1520f528-b819-49ef-bf04-734bfb918cc4",
"normalizedStrSet": ["4:1"]
}
}
@@ -4847,10 +4847,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Practice 3.5",
@@ -4860,13 +4860,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -4945,36 +4945,36 @@
"translations_mapping": {
"content": {
"hi": {
- "data_format": "",
- "translation": "\u201cAb,\u201d Uncle Berry ne kaha, \u201cmujhe is baat ka jawaab do. Is recipe ke hisaab se hamein doodh aur strawberry milana chahiye, jaise tasveer mein dikhaya gaya hai. Doodh se strawberry ka kya ratio hai?\u201d
",
+ "data_format": "html",
+ "translation": {"translation" : "\u201cAb,\u201d Uncle Berry ne kaha, \u201cmujhe is baat ka jawaab do. Is recipe ke hisaab se hamein doodh aur strawberry milana chahiye, jaise tasveer mein dikhaya gaya hai. Doodh se strawberry ka kya ratio hai?\u201d
"},
"needs_update": false
}
},
"feedback_2": {
"hi": {
- "data_format": "",
- "translation": "Yeh sahi ratio hai, lekin ek choti cheez: jab ratio type karo, tab tumhein jawab colon (\u201c:\u201d) istemaal karke likhna chahiye. Phir se koshish kariye!
",
+ "data_format": "html",
+ "translation": {"translation" : "Yeh sahi ratio hai, lekin ek choti cheez: jab ratio type karo, tab tumhein jawab colon (\u201c:\u201d) istemaal karke likhna chahiye. Phir se koshish kariye!
"},
"needs_update": false
}
},
"feedback_1": {
"hi": {
- "data_format": "",
- "translation": "Han, yeh sahi hai!
",
+ "data_format": "html",
+ "translation": {"translation" : "Han, yeh sahi hai!
"},
"needs_update": false
}
},
"default_outcome": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Phir se koshish kariye.
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Phir se koshish kariye.
"},
"needs_update": false
}
},
"feedback_3": {
"hi": {
- "data_format": "",
- "translation": "Nahin, yeh sahi nahin hai. Order pe dhyaan do. Sawaal doodh se strawberry ke ratio ki baat kar raha hai, lekin 4:1 strawberry se doodh ka ratio hai. Order pe dhyaan do!
",
+ "data_format": "html",
+ "translation": {"translation" : "Nahin, yeh sahi nahin hai. Order pe dhyaan do. Sawaal doodh se strawberry ke ratio ki baat kar raha hai, lekin 4:1 strawberry se doodh ka ratio hai. Order pe dhyaan do!
"},
"needs_update": false
}
}
diff --git a/domain/src/main/assets/2mzzFVDLuAj8.textproto b/domain/src/main/assets/2mzzFVDLuAj8.textproto
index 17a05ea1873..b1b187987d3 100644
--- a/domain/src/main/assets/2mzzFVDLuAj8.textproto
+++ b/domain/src/main/assets/2mzzFVDLuAj8.textproto
@@ -173,7 +173,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "71b5c0d8-2c30-4bf8-ac21-dd42774c4b95"
normalized_strings: "2:1"
}
}
@@ -194,7 +194,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "4d5dbaff-8f03-4b9e-b3e6-241ec4a8827b"
normalized_strings: "3"
}
}
@@ -215,7 +215,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "4992c409-75ec-4729-b7f0-d4a7d18b5c92"
normalized_strings: "1:2"
}
}
@@ -240,8 +240,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer as X:Y"
+ subtitled_unicode {
+ unicode_str: "Enter your answer as X:Y"
content_id: "ca_placeholder_0"
}
}
@@ -484,7 +484,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "8d2b9eea-c973-4bd6-a7e0-b1a7ae5603be"
normalized_strings: "2:3"
}
}
@@ -505,7 +505,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "c3c47717-3fdb-4d8d-9bfe-3dc72d19b8da"
normalized_strings: "2 to 3"
}
}
@@ -526,7 +526,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "a2431f83-2e3a-433c-8863-fe9dc3a75b98"
normalized_strings: "3:2"
}
}
@@ -547,7 +547,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "428fab4b-2a57-40e5-b025-9ffc11def1f4"
normalized_strings: "1"
}
}
@@ -578,8 +578,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer as X:Y"
+ subtitled_unicode {
+ unicode_str: "Enter your answer as X:Y"
content_id: "ca_placeholder_0"
}
}
@@ -834,7 +834,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "20c3a4f5-b4c7-4fe6-9780-f7e01b0e532c"
normalized_strings: "2:4:3:2"
}
}
@@ -855,7 +855,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "75b41c64-7309-4a2e-a78e-45a0ca35ffab"
normalized_strings: "2:4:3:1"
}
}
@@ -886,8 +886,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter the correct ratio."
+ subtitled_unicode {
+ unicode_str: "Enter the correct ratio."
content_id: "ca_placeholder_0"
}
}
@@ -1100,7 +1100,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "fe13e3d8-4fc6-4863-a3ce-98c710a1ec82"
normalized_strings: "2:1:3"
}
}
@@ -1121,7 +1121,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "cb9437b2-2931-4cc7-9cbe-ecb31ac22ad0"
normalized_strings: "2:1"
}
}
@@ -1142,7 +1142,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "9ce00383-554d-4606-abcb-31d020799768"
normalized_strings: "1:3"
}
}
@@ -1163,7 +1163,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "0fe368d4-5459-4535-94de-4c311afbfda1"
normalized_strings: "1:2:3"
}
}
@@ -1175,7 +1175,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "b9eb65eb-8f9f-4c5b-80cf-c92c5fa6141f"
normalized_strings: "1:3:2"
}
}
@@ -1187,7 +1187,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "468469f4-2a78-4e6c-abff-5004a158eae5"
normalized_strings: "2:3:1"
}
}
@@ -1199,7 +1199,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "d61214b1-8e64-4f9a-bdcb-3f0c56e60960"
normalized_strings: "3:1:2"
}
}
@@ -1211,7 +1211,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "150ad47d-cdce-4292-a2ac-17075efeb4a4"
normalized_strings: "3:2:1"
}
}
@@ -1236,8 +1236,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer as X:Y:Z"
+ subtitled_unicode {
+ unicode_str: "Enter your answer as X:Y:Z"
content_id: "ca_placeholder_0"
}
}
@@ -1575,7 +1575,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "866a20f8-a118-4058-933b-b6a91afd73b2"
normalized_strings: "2:3"
}
}
@@ -1597,7 +1597,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "75c4e32d-2357-46c8-8a1c-74dce83593fa"
normalized_strings: "2 : 3"
}
}
@@ -1609,7 +1609,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "fb41c73e-a2e7-47ea-90f2-26319ca611cc"
normalized_strings: "2: 3"
}
}
@@ -1621,7 +1621,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "156e3d22-7dfa-457d-b1d2-7ab26366349b"
normalized_strings: "2 :3"
}
}
@@ -1642,7 +1642,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "c33e0c96-28f8-4656-8760-f9684f61469f"
normalized_strings: "3:2"
}
}
@@ -1663,7 +1663,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "412b761e-74df-4161-a122-ad3bbe3908c2"
normalized_strings: "to"
}
}
@@ -1684,7 +1684,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "ab884bed-5685-4031-b9d2-0dd085b43f50"
normalized_strings: "X"
}
}
@@ -1696,7 +1696,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "068691b9-2fcd-4d50-987b-a8e588b0a0c8"
normalized_strings: "Y"
}
}
@@ -1717,7 +1717,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "763bf316-e64c-4196-993d-71df36487bdd"
normalized_strings: ":"
}
}
@@ -1742,8 +1742,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer in the form X:Y"
+ subtitled_unicode {
+ unicode_str: "Enter your answer in the form X:Y"
content_id: "ca_placeholder_0"
}
}
@@ -2513,7 +2513,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "783afa39-0a6e-4eaa-9876-9a9833f1705c"
normalized_strings: "2 to 5"
}
}
@@ -2525,7 +2525,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "3c2eb40b-911e-4023-8667-dc6a82b038c7"
normalized_strings: "2:5"
}
}
@@ -2537,7 +2537,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "4819cb3d-38c6-46ab-86fa-fba0f61ef821"
normalized_strings: "\"2 to 5\""
}
}
@@ -2558,7 +2558,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "cccb5b43-3764-4378-a7dd-de2151819128"
normalized_strings: "5 to 2"
}
}
@@ -2579,7 +2579,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "6cf25801-0344-466d-86b1-3fd0b3cb4373"
normalized_strings: "2to5"
}
}
@@ -2591,7 +2591,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "d649fc32-9e83-4ef6-a152-42f978ab9eee"
normalized_strings: "2 to5"
}
}
@@ -2603,7 +2603,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "f7783e38-65cd-4c09-8612-a28f24a5ec37"
normalized_strings: "2to 5"
}
}
@@ -2624,7 +2624,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "bba5b144-9374-40b4-8297-5aa3741d77ac"
normalized_strings: "X"
}
}
@@ -2636,7 +2636,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "b1e5f523-fe35-470d-a094-28dd1da45c8d"
normalized_strings: "Y"
}
}
@@ -2657,7 +2657,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "7c26d744-cfef-4184-a42a-a8412133ee1f"
normalized_strings: "to"
}
}
@@ -2682,8 +2682,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer as X to Y"
+ subtitled_unicode {
+ unicode_str: "Enter your answer as X to Y"
content_id: "ca_placeholder_0"
}
}
@@ -2961,7 +2961,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "9f99e701-3efe-4254-8fdc-3827d9ba498a"
normalized_strings: "50:20"
}
}
@@ -2973,7 +2973,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "1bdb2184-acc7-4b05-a024-5a975a796067"
normalized_strings: "5:2"
}
}
@@ -2994,7 +2994,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "724f7a4a-70aa-4a42-aab0-e1497424f465"
normalized_strings: "50 to 20"
}
}
@@ -3006,7 +3006,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "14186351-eee4-40fa-8f20-960306cfe465"
normalized_strings: "5 to 2"
}
}
@@ -3027,7 +3027,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "63181669-ee99-438e-8088-7308bcb55e95"
normalized_strings: "20:50"
}
}
@@ -3039,7 +3039,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "515833f7-3e0e-4eb4-a12e-629d95c43478"
normalized_strings: "2:5"
}
}
@@ -3060,7 +3060,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "54af7fbe-67fb-43d7-946c-920648a0a414"
normalized_strings: "50: 20"
}
}
@@ -3072,7 +3072,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "074938e1-b391-4d08-8d5f-ac14567cb05c"
normalized_strings: "50 :20"
}
}
@@ -3084,7 +3084,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "f9d87552-db3c-40a1-a9b6-d4748dc3c51b"
normalized_strings: "50 : 20"
}
}
@@ -3096,7 +3096,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "84be357c-d3f7-46c1-9b5a-527bbbb96058"
normalized_strings: "5: 2"
}
}
@@ -3108,7 +3108,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "5101996c-3a50-4dbe-8ffc-dfb8f0ee564e"
normalized_strings: "5 :2"
}
}
@@ -3120,7 +3120,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "d3299662-c092-44cb-95b3-b07dc0cf50fa"
normalized_strings: "5 : 2"
}
}
@@ -3145,8 +3145,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer in the form \"X:Y\""
+ subtitled_unicode {
+ unicode_str: "Enter your answer in the form \"X:Y\""
content_id: "ca_placeholder_0"
}
}
@@ -3389,7 +3389,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "c2b0772b-c38a-44c5-a14a-fe680a65a9e8"
normalized_strings: "3 to 1"
}
}
@@ -3401,7 +3401,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "bac00fc3-4953-4049-9dbe-52b04b2e85cf"
normalized_strings: "3:1"
}
}
@@ -3413,7 +3413,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "29006547-391f-474a-ac64-dbb2ab0de333"
normalized_strings: "\"3 to 1\""
}
}
@@ -3434,7 +3434,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "3bc9759f-b033-4dbf-a550-4670da8ecf3b"
normalized_strings: "1 to 3"
}
}
@@ -3455,7 +3455,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "992b6fb4-4550-4278-a083-8e3f8580410e"
normalized_strings: "3to1"
}
}
@@ -3467,7 +3467,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "c78bf24e-b510-4bac-a107-28a03dfa1c22"
normalized_strings: "3 to1"
}
}
@@ -3479,7 +3479,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "338cb82a-5e6f-460a-8fa0-5a2510c1a31e"
normalized_strings: "3to 1"
}
}
@@ -3500,7 +3500,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "7e96afac-4b8d-4a10-94bb-a146c5078c94"
normalized_strings: "X"
}
}
@@ -3512,7 +3512,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "1c480baf-c5a9-43e3-a746-27febfe4e818"
normalized_strings: "Y"
}
}
@@ -3533,7 +3533,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "930b1d9c-2b0e-4c89-8247-a0665db536af"
normalized_strings: "to"
}
}
@@ -3558,8 +3558,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer in the form \"X to Y\""
+ subtitled_unicode {
+ unicode_str: "Enter your answer in the form \"X to Y\""
content_id: "ca_placeholder_0"
}
}
@@ -3772,7 +3772,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "d4f1cd28-f796-4b32-9080-cf2c283039bf"
normalized_strings: "2:1"
}
}
@@ -3784,7 +3784,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "4161eeb4-38ae-43ca-8d06-3f62f6318c75"
normalized_strings: "1:1/2"
}
}
@@ -3796,7 +3796,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "72c48438-98eb-4cd5-b6dc-dca3034c98d5"
normalized_strings: "1:0.5"
}
}
@@ -3817,7 +3817,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "30bfedac-7ea5-44b9-95ef-3aeb08ba5d07"
normalized_strings: "1"
}
}
@@ -3838,7 +3838,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "1bfd83da-8e96-488a-84c6-985d1367af6e"
normalized_strings: "2: 1"
}
}
@@ -3859,7 +3859,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "44624b4f-5531-4e6f-8a91-539354be9596"
normalized_strings: "2:"
}
}
@@ -3884,8 +3884,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer as X:Y"
+ subtitled_unicode {
+ unicode_str: "Enter your answer as X:Y"
content_id: "ca_placeholder_0"
}
}
@@ -4068,7 +4068,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "9188484d-a59e-4932-9936-6d54e287f152"
normalized_strings: "1:4"
}
}
@@ -4080,7 +4080,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "1abb261a-14b8-4d67-850e-a1f173e0c824"
normalized_strings: "2:8"
}
}
@@ -4101,7 +4101,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "9e160745-9991-4651-b1f3-fc46922b1d92"
normalized_strings: "to"
}
}
@@ -4132,8 +4132,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer as X:Y"
+ subtitled_unicode {
+ unicode_str: "Enter your answer as X:Y"
content_id: "ca_placeholder_0"
}
}
@@ -5920,7 +5920,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "2218f74f-cb3d-4b3d-8f7f-48901af0f33d"
normalized_strings: "1:4"
}
}
@@ -5941,7 +5941,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "f498979c-a3cf-4a31-889a-b86aed44cfb5"
normalized_strings: "1 to 4"
}
}
@@ -5962,7 +5962,7 @@ states {
key: "x"
value {
translatable_set_of_normalized_string {
- content_id: ""
+ content_id: "1520f528-b819-49ef-bf04-734bfb918cc4"
normalized_strings: "4:1"
}
}
@@ -5987,8 +5987,8 @@ states {
key: "placeholder"
value {
custom_schema_value {
- subtitled_html {
- html: "Enter your answer as X:Y"
+ subtitled_unicode {
+ unicode_str: "Enter your answer as X:Y"
content_id: "ca_placeholder_0"
}
}
diff --git a/domain/src/main/assets/5NWuolNcwH6e.json b/domain/src/main/assets/5NWuolNcwH6e.json
index 4e00a2c425f..ff48e14c500 100644
--- a/domain/src/main/assets/5NWuolNcwH6e.json
+++ b/domain/src/main/assets/5NWuolNcwH6e.json
@@ -2,7 +2,7 @@
"exploration_id": "5NWuolNcwH6e",
"preferred_audio_language_code": "",
"correctness_feedback_enabled": false,
- "version": 77,
+ "version": 0,
"record_playthrough_probability": 0.0,
"exploration": {
"init_state_name": "Introduction",
@@ -31,13 +31,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -82,13 +82,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -122,14 +122,14 @@
"customization_args": {
"choices": {
"value": [{
- "html": "The ratio of strawberry puree to blueberry puree is 1:3 .
",
- "content_id": "ca_choices_0"
+ "content_id": "ca_choices_0",
+ "html": "The ratio of strawberry puree to blueberry puree is 1:3 .
"
}, {
- "html": "The ratio of strawberry puree to blueberry puree is 3:1 .
",
- "content_id": "ca_choices_1"
+ "content_id": "ca_choices_1",
+ "html": "The ratio of strawberry puree to blueberry puree is 3:1 .
"
}, {
- "html": "It's impossible to figure out the new ratio.
",
- "content_id": "ca_choices_2"
+ "content_id": "ca_choices_2",
+ "html": "It's impossible to figure out the new ratio.
"
}]
}
},
@@ -148,10 +148,10 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
@@ -167,10 +167,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Ratio when the order is changed",
@@ -180,13 +180,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -244,7 +244,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -258,7 +258,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "e7cfd169-ab4f-4c33-af4b-661a99e92fe8",
"normalizedStrSet": ["2:3:4"]
}
}
@@ -271,16 +271,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "98a24649-76c6-45fd-beed-efc786bc6673",
"normalizedStrSet": ["2:3:4:5"]
}
}
@@ -293,10 +293,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Final Test",
@@ -306,13 +306,13 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -370,7 +370,7 @@
"id": "TextInput",
"customization_args": {
"rows": {
- "value": 1.0
+ "value": 1
},
"placeholder": {
"value": {
@@ -384,7 +384,7 @@
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "0c01e275-dbc5-47ab-a4d2-4011c8dfb8ed",
"normalizedStrSet": ["3:2:4"]
}
}
@@ -397,16 +397,16 @@
},
"labelled_as_correct": true,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}, {
"rule_specs": [{
"rule_type": "Equals",
"inputs": {
"x": {
- "contentId": "",
+ "contentId": "c028cae8-45c9-4abe-b1ad-da0998d30ce3",
"normalizedStrSet": ["3:2:2:4"]
}
}
@@ -419,10 +419,10 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
- "tagged_skill_misconception_id": null
+ "tagged_skill_misconception_id": ""
}],
"default_outcome": {
"dest": "Review 4",
@@ -432,8 +432,8 @@
},
"labelled_as_correct": false,
"param_changes": [],
- "refresher_exploration_id": null,
- "missing_prerequisite_skill_id": null
+ "refresher_exploration_id": "",
+ "missing_prerequisite_skill_id": ""
},
"hints": [{
"hint_content": {
@@ -443,7 +443,7 @@
}],
"solution": null
},
- "classifier_model_id": null,
+ "classifier_model_id": "",
"recorded_voiceovers": {
"voiceovers_mapping": {
"content": {
@@ -511,17 +511,17 @@
"customization_args": {
"choices": {
"value": [{
- "html": "For any amount of blueberry puree, we need to use twice that amount of milk.
",
- "content_id": "ca_choices_0"
+ "content_id": "ca_choices_0",
+ "html": "For any amount of blueberry puree, we need to use twice that amount of milk.
"
}, {
- "html": "We need