diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3618a9bbbfe..c86ff811456 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -64,7 +64,7 @@ - + diff --git a/app/src/main/java/org/oppia/app/activity/ActivityComponent.kt b/app/src/main/java/org/oppia/app/activity/ActivityComponent.kt index 7cf88bd3dcd..8eeac18d9bb 100644 --- a/app/src/main/java/org/oppia/app/activity/ActivityComponent.kt +++ b/app/src/main/java/org/oppia/app/activity/ActivityComponent.kt @@ -21,7 +21,7 @@ import org.oppia.app.testing.HtmlParserTestActivity import org.oppia.app.testing.TopicTestActivity import org.oppia.app.testing.TopicTestActivityForStory import org.oppia.app.topic.TopicActivity -import org.oppia.app.topic.conceptcard.testing.ConceptCardFragmentTestActivity +import org.oppia.app.testing.ConceptCardFragmentTestActivity import org.oppia.app.topic.questionplayer.QuestionPlayerActivity import javax.inject.Provider diff --git a/app/src/main/java/org/oppia/app/topic/conceptcard/testing/ConceptCardFragmentTestActivity.kt b/app/src/main/java/org/oppia/app/testing/ConceptCardFragmentTestActivity.kt similarity index 51% rename from app/src/main/java/org/oppia/app/topic/conceptcard/testing/ConceptCardFragmentTestActivity.kt rename to app/src/main/java/org/oppia/app/testing/ConceptCardFragmentTestActivity.kt index 24b6055970c..244d4e32e53 100644 --- a/app/src/main/java/org/oppia/app/topic/conceptcard/testing/ConceptCardFragmentTestActivity.kt +++ b/app/src/main/java/org/oppia/app/testing/ConceptCardFragmentTestActivity.kt @@ -1,11 +1,13 @@ -package org.oppia.app.topic.conceptcard.testing +package org.oppia.app.testing import android.os.Bundle import org.oppia.app.activity.InjectableAppCompatActivity +import org.oppia.app.topic.conceptcard.ConceptCardFragment +import org.oppia.app.topic.conceptcard.ConceptCardListener import javax.inject.Inject /** Test Activity used for testing ConceptCardFragment */ -class ConceptCardFragmentTestActivity : InjectableAppCompatActivity() { +class ConceptCardFragmentTestActivity : InjectableAppCompatActivity(), ConceptCardListener { @Inject lateinit var conceptCardFragmentTestActivityController: ConceptCardFragmentTestActivityPresenter @@ -14,4 +16,16 @@ class ConceptCardFragmentTestActivity : InjectableAppCompatActivity() { activityComponent.inject(this) conceptCardFragmentTestActivityController.handleOnCreate() } + + override fun dismiss() { + getConceptCardFragment()?.dismiss() + } + + private fun getConceptCardFragment(): ConceptCardFragment? { + return supportFragmentManager.findFragmentByTag(TAG_CONCEPT_CARD_DIALOG) as ConceptCardFragment? + } + + companion object { + internal const val TAG_CONCEPT_CARD_DIALOG = "CONCEPT_CARD_DIALOG" + } } diff --git a/app/src/main/java/org/oppia/app/topic/conceptcard/testing/ConceptCardFragmentTestActivityPresenter.kt b/app/src/main/java/org/oppia/app/testing/ConceptCardFragmentTestActivityPresenter.kt similarity index 74% rename from app/src/main/java/org/oppia/app/topic/conceptcard/testing/ConceptCardFragmentTestActivityPresenter.kt rename to app/src/main/java/org/oppia/app/testing/ConceptCardFragmentTestActivityPresenter.kt index 68d16f32bf4..9dacd589cf9 100644 --- a/app/src/main/java/org/oppia/app/topic/conceptcard/testing/ConceptCardFragmentTestActivityPresenter.kt +++ b/app/src/main/java/org/oppia/app/testing/ConceptCardFragmentTestActivityPresenter.kt @@ -1,25 +1,24 @@ -package org.oppia.app.topic.conceptcard.testing +package org.oppia.app.testing import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.concept_card_fragment_test_activity.* import org.oppia.app.R +import org.oppia.app.testing.ConceptCardFragmentTestActivity.Companion.TAG_CONCEPT_CARD_DIALOG import org.oppia.app.topic.conceptcard.ConceptCardFragment +import org.oppia.domain.topic.TEST_SKILL_ID_0 import org.oppia.domain.topic.TEST_SKILL_ID_1 -import org.oppia.domain.topic.TEST_SKILL_ID_2 import javax.inject.Inject -private const val TAG_CONCEPT_CARD_DIALOG = "CONCEPT_CARD_DIALOG" - /** The presenter for [ConceptCardFragmentTestActivity] */ class ConceptCardFragmentTestActivityPresenter @Inject constructor(private val activity: AppCompatActivity) { fun handleOnCreate() { activity.setContentView(R.layout.concept_card_fragment_test_activity) - activity.open_dialog_1.setOnClickListener { - val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_1) + activity.open_dialog_0.setOnClickListener { + val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_0) frag.showNow(activity.supportFragmentManager, TAG_CONCEPT_CARD_DIALOG) } - activity.open_dialog_2.setOnClickListener { - val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_2) + activity.open_dialog_1.setOnClickListener { + val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_1) frag.showNow(activity.supportFragmentManager, TAG_CONCEPT_CARD_DIALOG) } } diff --git a/app/src/main/java/org/oppia/app/testing/TopicTestActivity.kt b/app/src/main/java/org/oppia/app/testing/TopicTestActivity.kt index cd5cf99dd53..4a076283dd5 100644 --- a/app/src/main/java/org/oppia/app/testing/TopicTestActivity.kt +++ b/app/src/main/java/org/oppia/app/testing/TopicTestActivity.kt @@ -14,13 +14,14 @@ import org.oppia.app.topic.TopicActivityPresenter import org.oppia.app.topic.TopicFragment import org.oppia.app.topic.TopicTab import org.oppia.app.topic.conceptcard.ConceptCardFragment +import org.oppia.app.topic.conceptcard.ConceptCardListener import org.oppia.app.topic.questionplayer.QuestionPlayerActivity import org.oppia.domain.topic.TEST_TOPIC_ID_0 import javax.inject.Inject /** The activity for testing [TopicFragment]. */ class TopicTestActivity : InjectableAppCompatActivity(), RouteToQuestionPlayerListener, RouteToConceptCardListener, - RouteToTopicPlayListener, RouteToStoryListener, RouteToExplorationListener { + RouteToTopicPlayListener, RouteToStoryListener, RouteToExplorationListener, ConceptCardListener { @Inject lateinit var topicActivityPresenter: TopicActivityPresenter @@ -50,6 +51,10 @@ class TopicTestActivity : InjectableAppCompatActivity(), RouteToQuestionPlayerLi } } + override fun dismiss() { + getConceptCardFragment()?.dismiss() + } + override fun routeToExploration(explorationId: String) { startActivity(ExplorationActivity.createExplorationActivityIntent(this, explorationId)) } diff --git a/app/src/main/java/org/oppia/app/testing/TopicTestActivityForStory.kt b/app/src/main/java/org/oppia/app/testing/TopicTestActivityForStory.kt index 966aa4ec590..8ddecadc5a6 100644 --- a/app/src/main/java/org/oppia/app/testing/TopicTestActivityForStory.kt +++ b/app/src/main/java/org/oppia/app/testing/TopicTestActivityForStory.kt @@ -14,6 +14,7 @@ import org.oppia.app.topic.TopicActivityPresenter import org.oppia.app.topic.TopicFragment import org.oppia.app.topic.TopicTab import org.oppia.app.topic.conceptcard.ConceptCardFragment +import org.oppia.app.topic.conceptcard.ConceptCardListener import org.oppia.app.topic.questionplayer.QuestionPlayerActivity import org.oppia.domain.topic.TEST_STORY_ID_1 import org.oppia.domain.topic.TEST_TOPIC_ID_0 @@ -21,8 +22,8 @@ import javax.inject.Inject /** The test activity for [TopicFragment] to test displaying story by storyId. */ class TopicTestActivityForStory : InjectableAppCompatActivity(), RouteToQuestionPlayerListener, - RouteToConceptCardListener, - RouteToTopicPlayListener, RouteToStoryListener, RouteToExplorationListener { + RouteToConceptCardListener, RouteToTopicPlayListener, RouteToStoryListener, + RouteToExplorationListener, ConceptCardListener { @Inject lateinit var topicActivityPresenter: TopicActivityPresenter @@ -52,6 +53,10 @@ class TopicTestActivityForStory : InjectableAppCompatActivity(), RouteToQuestion } } + override fun dismiss() { + getConceptCardFragment()?.dismiss() + } + override fun routeToExploration(explorationId: String) { startActivity(ExplorationActivity.createExplorationActivityIntent(this, explorationId)) } diff --git a/app/src/main/java/org/oppia/app/topic/TopicActivity.kt b/app/src/main/java/org/oppia/app/topic/TopicActivity.kt index f296540852a..a3516c769f4 100644 --- a/app/src/main/java/org/oppia/app/topic/TopicActivity.kt +++ b/app/src/main/java/org/oppia/app/topic/TopicActivity.kt @@ -8,6 +8,7 @@ import org.oppia.app.home.RouteToExplorationListener import org.oppia.app.player.exploration.ExplorationActivity import org.oppia.app.story.StoryActivity import org.oppia.app.topic.conceptcard.ConceptCardFragment +import org.oppia.app.topic.conceptcard.ConceptCardListener import org.oppia.app.topic.questionplayer.QuestionPlayerActivity import javax.inject.Inject @@ -15,7 +16,7 @@ const val TOPIC_ACTIVITY_TOPIC_ID_ARGUMENT_KEY = "TopicActivity.topic_id" /** The activity for displaying [TopicFragment]. */ class TopicActivity : InjectableAppCompatActivity(), RouteToQuestionPlayerListener, RouteToConceptCardListener, - RouteToTopicPlayListener, RouteToStoryListener, RouteToExplorationListener { + RouteToTopicPlayListener, RouteToStoryListener, RouteToExplorationListener, ConceptCardListener { private lateinit var topicId: String private lateinit var storyId: String @Inject @@ -52,6 +53,10 @@ class TopicActivity : InjectableAppCompatActivity(), RouteToQuestionPlayerListen } } + override fun dismiss() { + getConceptCardFragment()?.dismiss() + } + override fun routeToExploration(explorationId: String) { startActivity(ExplorationActivity.createExplorationActivityIntent(this, explorationId)) } diff --git a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragment.kt b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragment.kt index a47cdddcc0d..b88f12c2da3 100644 --- a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragment.kt +++ b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragment.kt @@ -29,7 +29,7 @@ class ConceptCardFragment : InjectableDialogFragment() { } } - @Inject lateinit var conceptCardPresenter: ConceptCardPresenter + @Inject lateinit var conceptCardFragmentPresenter: ConceptCardFragmentPresenter override fun onAttach(context: Context) { super.onAttach(context) @@ -45,7 +45,7 @@ class ConceptCardFragment : InjectableDialogFragment() { super.onCreateView(inflater, container, savedInstanceState) val args = checkNotNull(arguments) { "Expected arguments to be passed to ConceptCardFragment" } val skillId = checkNotNull(args.getString(KEY_SKILL_ID)) { "Expected skillId to be passed to ConceptCardFragment" } - return conceptCardPresenter.handleCreateView(inflater, container, skillId) + return conceptCardFragmentPresenter.handleCreateView(inflater, container, skillId) } override fun onStart() { diff --git a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardPresenter.kt b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentPresenter.kt similarity index 58% rename from app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardPresenter.kt rename to app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentPresenter.kt index 5284ded98b4..a0868ded824 100644 --- a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardPresenter.kt +++ b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardFragmentPresenter.kt @@ -3,35 +3,35 @@ package org.oppia.app.topic.conceptcard import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import org.oppia.app.R -import org.oppia.app.databinding.ConceptCardExampleViewBinding import org.oppia.app.databinding.ConceptCardFragmentBinding import org.oppia.app.fragment.FragmentScope -import org.oppia.app.model.SubtitledHtml -import org.oppia.app.recyclerview.BindableAdapter import org.oppia.app.viewmodel.ViewModelProvider import javax.inject.Inject /** Presenter for [ConceptCardFragment], sets up bindings from ViewModel */ @FragmentScope -class ConceptCardPresenter @Inject constructor( +class ConceptCardFragmentPresenter @Inject constructor( private val fragment: Fragment, private val viewModelProvider: ViewModelProvider -){ +) { + private lateinit var skillId: String - /** Sets up data binding and adapter for RecyclerView */ - fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?, skillId: String): View? { - val viewModel = getConceptCardViewModel() - viewModel.setSkillId(skillId) + /** + * Sets up data binding and toolbar. + * Host activity must inherit ConceptCardListener to dismiss this fragment. + */ + fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?, id: String): View? { val binding = ConceptCardFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false) + val viewModel = getConceptCardViewModel() + + skillId = id + viewModel.setSkillIdAndBinding(skillId, binding) + binding.conceptCardToolbar.setNavigationIcon(R.drawable.ic_close_white_24dp) binding.conceptCardToolbar.setNavigationOnClickListener { - (fragment as? DialogFragment)?.dismiss() - } - binding.workedExamples.apply { - adapter = createRecyclerViewAdapter() + (fragment.requireActivity() as? ConceptCardListener)?.dismiss() } binding.let { @@ -44,13 +44,4 @@ class ConceptCardPresenter @Inject constructor( private fun getConceptCardViewModel(): ConceptCardViewModel { return viewModelProvider.getForFragment(fragment, ConceptCardViewModel::class.java) } - - private fun createRecyclerViewAdapter(): BindableAdapter { - return BindableAdapter.Builder - .newBuilder() - .registerViewDataBinderWithSameModelType( - inflateDataBinding = ConceptCardExampleViewBinding::inflate, - setViewModel = ConceptCardExampleViewBinding::setSubtitledHtml) - .build() - } } diff --git a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardListener.kt b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardListener.kt new file mode 100644 index 00000000000..7901f4006e7 --- /dev/null +++ b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardListener.kt @@ -0,0 +1,7 @@ +package org.oppia.app.topic.conceptcard + +/** Allows parent activity to dismiss the [ConceptCardFragment] */ +interface ConceptCardListener { + /** Called when the concept card dialog should be dismissed. */ + fun dismiss() +} diff --git a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt index 73a3822f1d3..a928b6bd5cd 100644 --- a/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt +++ b/app/src/main/java/org/oppia/app/topic/conceptcard/ConceptCardViewModel.kt @@ -1,38 +1,42 @@ package org.oppia.app.topic.conceptcard +import android.widget.TextView import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations import androidx.lifecycle.ViewModel +import org.oppia.app.databinding.ConceptCardFragmentBinding import org.oppia.app.fragment.FragmentScope import org.oppia.app.model.ConceptCard -import org.oppia.app.model.SubtitledHtml import org.oppia.domain.topic.TopicController import org.oppia.util.data.AsyncResult import org.oppia.util.logging.Logger +import org.oppia.util.parser.ConceptCardHtmlParserEntityType +import org.oppia.util.parser.HtmlParser import javax.inject.Inject /** [ViewModel] for concept card, providing rich text and worked examples */ @FragmentScope class ConceptCardViewModel @Inject constructor( private val topicController: TopicController, - private val logger: Logger + private val logger: Logger, + private val htmlParserFactory: HtmlParser.Factory, + @ConceptCardHtmlParserEntityType private val entityType: String ) : ViewModel() { - private lateinit var skillId: String + private lateinit var binding: ConceptCardFragmentBinding - /** Live Data for concept card explanation */ val conceptCardLiveData: LiveData by lazy { processConceptCardLiveData() } - /** Live Data for concept card worked examples. */ - val workedExamplesLiveData: LiveData> by lazy { - processWorkedExamplesLiveData() + val explanationLiveData: LiveData by lazy { + processExplanationLiveData() } - /** Sets the value of skillId. Must be called before setting ViewModel to binding. */ - fun setSkillId(id: String) { + /** Sets the value of skillId and binding. Must be called before setting ViewModel to binding */ + fun setSkillIdAndBinding(id: String, binding: ConceptCardFragmentBinding) { skillId = id + this.binding = binding } private val conceptCardResultLiveData: LiveData> by lazy { @@ -43,21 +47,23 @@ class ConceptCardViewModel @Inject constructor( return Transformations.map(conceptCardResultLiveData, ::processConceptCardResult) } - private fun processWorkedExamplesLiveData(): LiveData> { - return Transformations.map(conceptCardResultLiveData, ::processConceptCardWorkExamples) + private fun processExplanationLiveData(): LiveData { + return Transformations.map(conceptCardResultLiveData, ::processExplanationResult) } private fun processConceptCardResult(conceptCardResult: AsyncResult): ConceptCard { if (conceptCardResult.isFailure()) { - logger.e("ConceptCardFragment", "Failed to retrieve Concept Card: " + conceptCardResult.getErrorOrNull()) + logger.e("ConceptCardFragment", "Failed to retrieve Concept Card", conceptCardResult.getErrorOrNull()!!) } return conceptCardResult.getOrDefault(ConceptCard.getDefaultInstance()) } - private fun processConceptCardWorkExamples(conceptCardResult: AsyncResult): List { + private fun processExplanationResult(conceptCardResult: AsyncResult): CharSequence { if (conceptCardResult.isFailure()) { - logger.e("ConceptCardFragment", "Failed to retrieve Concept Card: " + conceptCardResult.getErrorOrNull()) + logger.e("ConceptCardFragment", "Failed to retrieve Concept Card", conceptCardResult.getErrorOrNull()!!) } - return conceptCardResult.getOrDefault(ConceptCard.getDefaultInstance()).workedExampleList + val conceptCard = conceptCardResult.getOrDefault(ConceptCard.getDefaultInstance()) + return htmlParserFactory.create(entityType, skillId) + .parseOppiaHtml(conceptCard.explanation.html, binding.conceptCardExplanationText) } } diff --git a/app/src/main/res/layout/concept_card_example_view.xml b/app/src/main/res/layout/concept_card_example_view.xml deleted file mode 100644 index 89f420b19ef..00000000000 --- a/app/src/main/res/layout/concept_card_example_view.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/concept_card_fragment.xml b/app/src/main/res/layout/concept_card_fragment.xml index fe81eacd92b..ac5412a2955 100644 --- a/app/src/main/res/layout/concept_card_fragment.xml +++ b/app/src/main/res/layout/concept_card_fragment.xml @@ -16,36 +16,54 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorConceptToolbarBackground" + app:title="@string/concept_card_toolbar_title" + app:titleTextColor="@color/white" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" /> + + app:layout_constraintTop_toTopOf="@+id/toolbar_shadow_view" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent"> + android:orientation="vertical"> - + + android:layout_marginTop="28dp" + android:layout_marginStart="40dp" + android:layout_marginEnd="40dp" + android:text="@{viewModel.explanationLiveData}" + android:textSize="16sp" + android:textColor="@color/oppiaPrimaryText" + android:fontFamily="sans-serif" /> + diff --git a/app/src/main/res/layout/concept_card_fragment_test_activity.xml b/app/src/main/res/layout/concept_card_fragment_test_activity.xml index df200985f2f..245c1497c66 100644 --- a/app/src/main/res/layout/concept_card_fragment_test_activity.xml +++ b/app/src/main/res/layout/concept_card_fragment_test_activity.xml @@ -4,12 +4,12 @@ xmlns:android="http://schemas.android.com/apk/res/android">