diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1e435dfc707..e7ef3e04575 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -35,4 +35,4 @@
android:theme="@style/SplashScreenTheme" />
-
\ No newline at end of file
+
diff --git a/app/src/main/java/org/oppia/app/application/ApplicationComponent.kt b/app/src/main/java/org/oppia/app/application/ApplicationComponent.kt
index 6a041f9e040..8aaa1da7745 100644
--- a/app/src/main/java/org/oppia/app/application/ApplicationComponent.kt
+++ b/app/src/main/java/org/oppia/app/application/ApplicationComponent.kt
@@ -4,6 +4,7 @@ import android.app.Application
import dagger.BindsInstance
import dagger.Component
import org.oppia.app.activity.ActivityComponent
+import org.oppia.util.parser.HtmlParserEntityTypeModule
import org.oppia.data.backends.gae.NetworkModule
import org.oppia.domain.classify.InteractionsModule
import org.oppia.domain.classify.rules.continueinteraction.ContinueModule
@@ -28,7 +29,8 @@ import javax.inject.Singleton
ApplicationModule::class, DispatcherModule::class, NetworkModule::class, LoggerModule::class,
ContinueModule::class, FractionInputModule::class, ItemSelectionInputModule::class, MultipleChoiceInputModule::class,
NumberWithUnitsRuleModule::class, NumericInputRuleModule::class, TextInputRuleModule::class, InteractionsModule::class,
- GcsResourceModule::class, ImageParsingModule::class, HtmlParsingModule::class, ImageLoaderModule::class
+ GcsResourceModule::class, ImageParsingModule::class, HtmlParsingModule::class, ImageLoaderModule::class,
+ HtmlParserEntityTypeModule::class
])
interface ApplicationComponent {
@Component.Builder
diff --git a/app/src/main/java/org/oppia/app/player/state/StateAdapter.kt b/app/src/main/java/org/oppia/app/player/state/StateAdapter.kt
index 00356009f9d..9c606fcde33 100644
--- a/app/src/main/java/org/oppia/app/player/state/StateAdapter.kt
+++ b/app/src/main/java/org/oppia/app/player/state/StateAdapter.kt
@@ -1,5 +1,6 @@
package org.oppia.app.player.state
+import android.text.Spannable
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
@@ -14,6 +15,7 @@ import org.oppia.app.player.state.itemviewmodel.StateButtonViewModel
import org.oppia.app.player.state.listener.ButtonInteractionListener
import org.oppia.app.databinding.StateButtonItemBinding
import org.oppia.app.player.state.itemviewmodel.ContentViewModel
+import org.oppia.util.parser.HtmlParser
@Suppress("unused")
private const val VIEW_TYPE_CONTENT = 1
@@ -28,7 +30,10 @@ private const val VIEW_TYPE_STATE_BUTTON = 5
/** Adapter to inflate different items/views inside [RecyclerView]. The itemList consists of various ViewModels. */
class StateAdapter(
private val itemList: MutableList,
- private val buttonInteractionListener: ButtonInteractionListener
+ private val buttonInteractionListener: ButtonInteractionListener,
+ private val htmlParserFactory: HtmlParser.Factory,
+ private val entityType: String,
+ private val explorationId: String
) :
RecyclerView.Adapter() {
@@ -47,18 +52,19 @@ class StateAdapter(
/* attachToParent= */false
)
StateButtonViewHolder(binding, buttonInteractionListener)
- } VIEW_TYPE_CONTENT -> {
+ }
+ VIEW_TYPE_CONTENT -> {
val inflater = LayoutInflater.from(parent.context)
val binding =
DataBindingUtil.inflate(
inflater,
R.layout.content_item,
parent,
- /* attachToParent= */false
+ /* attachToParent= */ false
)
ContentViewHolder(binding)
}
- else -> throw IllegalArgumentException("Invalid view type") as Throwable
+ else -> throw IllegalArgumentException("Invalid view type")
}
}
@@ -68,8 +74,8 @@ class StateAdapter(
(holder as StateButtonViewHolder).bind(itemList[position] as StateButtonViewModel)
}
VIEW_TYPE_CONTENT -> {
- (holder as ContentViewHolder).bind((itemList[position] as ContentViewModel).htmlContent)
- }
+ (holder as ContentViewHolder).bind((itemList[position] as ContentViewModel).htmlContent)
+ }
}
}
@@ -87,13 +93,19 @@ class StateAdapter(
override fun getItemCount(): Int {
return itemList.size
}
+
inner class ContentViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
- internal fun bind(rawString: String?) {
+ internal fun bind(rawString: String) {
binding.setVariable(BR.htmlContent, rawString)
binding.executePendingBindings()
- binding.root.content_text_view.text = rawString
+ val htmlResult: Spannable = htmlParserFactory.create(entityType, explorationId).parseOppiaHtml(
+ rawString,
+ binding.root.content_text_view
+ )
+ binding.root.content_text_view.text = htmlResult
}
}
+
private class StateButtonViewHolder(
val binding: ViewDataBinding,
private val buttonInteractionListener: ButtonInteractionListener
diff --git a/app/src/main/java/org/oppia/app/player/state/StateFragmentPresenter.kt b/app/src/main/java/org/oppia/app/player/state/StateFragmentPresenter.kt
index c12eb395c17..a43b1f3c8b1 100755
--- a/app/src/main/java/org/oppia/app/player/state/StateFragmentPresenter.kt
+++ b/app/src/main/java/org/oppia/app/player/state/StateFragmentPresenter.kt
@@ -30,6 +30,8 @@ import org.oppia.domain.exploration.ExplorationDataController
import org.oppia.domain.exploration.ExplorationProgressController
import org.oppia.util.data.AsyncResult
import org.oppia.util.logging.Logger
+import org.oppia.util.parser.ExplorationHtmlParserEntityType
+import org.oppia.util.parser.HtmlParser
import javax.inject.Inject
const val STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY = "STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY"
@@ -54,6 +56,7 @@ private const val DEFAULT_CONTINUE_INTERACTION_TEXT_ANSWER = "Please continue."
/** The presenter for [StateFragment]. */
@FragmentScope
class StateFragmentPresenter @Inject constructor(
+ @ExplorationHtmlParserEntityType private val entityType: String,
private val activity: AppCompatActivity,
private val fragment: Fragment,
private val cellularDialogController: CellularDialogController,
@@ -61,7 +64,8 @@ class StateFragmentPresenter @Inject constructor(
private val viewModelProvider: ViewModelProvider,
private val explorationDataController: ExplorationDataController,
private val explorationProgressController: ExplorationProgressController,
- private val logger: Logger
+ private val logger: Logger,
+ private val htmlParserFactory: HtmlParser.Factory
) : ButtonInteractionListener {
private var showCellularDataDialog = true
@@ -92,9 +96,8 @@ class StateFragmentPresenter @Inject constructor(
useCellularData = prefs.useCellularData
}
})
-
- stateAdapter = StateAdapter(itemList, this as ButtonInteractionListener)
-
+ explorationId = fragment.arguments!!.getString(STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY)!!
+ stateAdapter = StateAdapter(itemList, this as ButtonInteractionListener, htmlParserFactory, entityType, explorationId)
binding = StateFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding.stateRecyclerView.apply {
adapter = stateAdapter
@@ -103,7 +106,6 @@ class StateFragmentPresenter @Inject constructor(
it.stateFragment = fragment as StateFragment
it.viewModel = getStateViewModel()
}
- explorationId = checkNotNull(fragment.arguments!!.getString(STATE_FRAGMENT_EXPLORATION_ID_ARGUMENT_KEY))
subscribeToCurrentState()
@@ -331,7 +333,7 @@ class StateFragmentPresenter @Inject constructor(
}
}
private fun checkAndAddContentItem() {
- if (currentEphemeralState!!.state.hasContent()) {
+ if (currentEphemeralState.state.hasContent()) {
addContentItem()
} else {
logger.e("StateFragment", "checkAndAddContentItem: State does not have content.")
@@ -340,7 +342,7 @@ class StateFragmentPresenter @Inject constructor(
private fun addContentItem() {
val contentViewModel = ContentViewModel()
- val contentSubtitledHtml: SubtitledHtml = currentEphemeralState!!.state.content
+ val contentSubtitledHtml: SubtitledHtml = currentEphemeralState.state.content
if (contentSubtitledHtml.contentId != "") {
contentViewModel.contentId = contentSubtitledHtml.contentId
} else {
diff --git a/app/src/sharedTest/java/org/oppia/app/player/state/StateFragmentContentCardTest.kt b/app/src/sharedTest/java/org/oppia/app/player/state/StateFragmentContentCardTest.kt
new file mode 100755
index 00000000000..88cc5d778f8
--- /dev/null
+++ b/app/src/sharedTest/java/org/oppia/app/player/state/StateFragmentContentCardTest.kt
@@ -0,0 +1,31 @@
+package org.oppia.app.player.state
+
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.oppia.app.R
+import org.oppia.app.home.HomeActivity
+import org.oppia.app.recyclerview.RecyclerViewMatcher.Companion.atPosition
+
+// TODO(#205): Add test case for image parsing once PR #205 is merged.
+/** Tests for [VIEW_TYPE_CONTENT]. */
+@RunWith(AndroidJUnit4::class)
+class StateFragmentContentCardTest {
+
+ @Test
+ fun testContentCard_forDemoExploration_withCustomOppiaTags_displaysParsedHtml() {
+ ActivityScenario.launch(HomeActivity::class.java).use {
+ onView(withId(R.id.play_exploration_button)).perform(click())
+ val htmlResult = "Hi, welcome to Oppia! is a tool that helps you create interactive learning activities that can be continually improved over time.\n\n" +
+ "Incidentally, do you know where the name 'Oppia' comes from?\n\n"
+ onView(atPosition(R.id.state_recycler_view, 0)).check(matches(hasDescendant(withText(htmlResult))))
+ }
+ }
+}
diff --git a/utility/src/main/java/org/oppia/util/parser/ExplorationHtmlParserEntityType.kt b/utility/src/main/java/org/oppia/util/parser/ExplorationHtmlParserEntityType.kt
new file mode 100644
index 00000000000..779fbc5e55e
--- /dev/null
+++ b/utility/src/main/java/org/oppia/util/parser/ExplorationHtmlParserEntityType.kt
@@ -0,0 +1,7 @@
+package org.oppia.util.parser
+
+import javax.inject.Qualifier
+
+/** Qualifier for injecting the entity type for exploration. */
+@Qualifier
+annotation class ExplorationHtmlParserEntityType
diff --git a/utility/src/main/java/org/oppia/util/parser/HtmlParserEntityTypeModule.kt b/utility/src/main/java/org/oppia/util/parser/HtmlParserEntityTypeModule.kt
new file mode 100644
index 00000000000..bb33a097778
--- /dev/null
+++ b/utility/src/main/java/org/oppia/util/parser/HtmlParserEntityTypeModule.kt
@@ -0,0 +1,14 @@
+package org.oppia.util.parser
+
+import dagger.Module
+import dagger.Provides
+
+/** Provides Html parsing entity type dependencies. */
+@Module
+class HtmlParserEntityTypeModule {
+ @Provides
+ @ExplorationHtmlParserEntityType
+ fun provideExplorationHtmlParserEntityType(): String {
+ return "exploration"
+ }
+}