Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #160: Integrating topic controller into Concept Card #198

Merged
merged 19 commits into from
Oct 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/OppiaTheme">
<activity android:name="org.oppia.app.topic.conceptcard.testing.ConceptCardFragmentTestActivity"/>
<activity android:name="org.oppia.app.player.state.testing.StateFragmentTestActivity"/>
<activity android:name=".player.exploration.ExplorationActivity"/>
<activity android:name=".topic.questionplayer.QuestionPlayerActivity"/>
<activity android:name=".topic.TopicActivity"/>

<activity android:name="org.oppia.app.player.state.testing.StateFragmentTestActivity"/>
<activity android:name="org.oppia.app.home.HomeActivity">
<intent-filter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dagger.Subcomponent
import org.oppia.app.fragment.FragmentComponent
import org.oppia.app.home.HomeActivity
import org.oppia.app.player.exploration.ExplorationActivity
import org.oppia.app.topic.conceptcard.testing.ConceptCardFragmentTestActivity
import org.oppia.app.player.state.testing.StateFragmentTestActivity
import org.oppia.app.testing.BindableAdapterTestActivity
import org.oppia.app.topic.TopicActivity
Expand All @@ -27,6 +28,7 @@ interface ActivityComponent {
fun inject(bindableAdapterTestActivity: BindableAdapterTestActivity)
fun inject(explorationActivity: ExplorationActivity)
fun inject(homeActivity: HomeActivity)
fun inject(conceptCardFragmentTestActivity: ConceptCardFragmentTestActivity)
fun inject(questionPlayerActivity: QuestionPlayerActivity)
fun inject(topicActivity: TopicActivity)
fun inject(stateFragmentTestActivity: StateFragmentTestActivity)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,26 @@ import org.oppia.app.R
import org.oppia.app.fragment.InjectableDialogFragment
import javax.inject.Inject

private const val KEY_SKILL_ID = "SKILL_ID"

/* Fragment that displays a fullscreen dialog for concept cards */
class ConceptCardFragment : InjectableDialogFragment() {

companion object {
/**
* Creates a new instance of a DialogFragment to display content
* @param skillId Used in TopicController to get correct concept card data.
* @return [ConceptCardFragment]: DialogFragment
*/
fun newInstance(skillId: String): ConceptCardFragment {
val conceptCardFrag = ConceptCardFragment()
val args = Bundle()
args.putString(KEY_SKILL_ID, skillId)
conceptCardFrag.arguments = args
return conceptCardFrag
}
}

@Inject lateinit var conceptCardPresenter: ConceptCardPresenter

override fun onAttach(context: Context?) {
Expand All @@ -25,7 +43,9 @@ class ConceptCardFragment : InjectableDialogFragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return conceptCardPresenter.handleCreateView(inflater, container)
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)
}

override fun onStart() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import org.oppia.app.R
import org.oppia.app.databinding.ConceptcardFragmentBinding
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

Expand All @@ -17,21 +20,37 @@ class ConceptCardPresenter @Inject constructor(
private val fragment: Fragment,
private val viewModelProvider: ViewModelProvider<ConceptCardViewModel>
){
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
val binding = ConceptcardFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)

/** Sets up data binding and adapter for RecyclerView */
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?, skillId: String): View? {
val viewModel = getConceptCardViewModel()
viewModel.setSkillId(skillId)
val binding = ConceptCardFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding.conceptCardToolbar.setNavigationIcon(R.drawable.ic_close_white_24dp)
binding.conceptCardToolbar.setNavigationOnClickListener {
(fragment as? DialogFragment)?.dismiss()
}
binding.workedExamples.apply {
adapter = createRecyclerViewAdapter()
}

binding.let {
it.viewModel = getConceptCardViewModel()
it.viewModel = viewModel
it.lifecycleOwner = fragment
}

return binding.root
}

private fun getConceptCardViewModel(): ConceptCardViewModel {
return viewModelProvider.getForFragment(fragment, ConceptCardViewModel::class.java)
}

private fun createRecyclerViewAdapter(): BindableAdapter<SubtitledHtml> {
return BindableAdapter.Builder
.newBuilder<SubtitledHtml>()
.registerViewDataBinder(
inflateDataBinding = ConceptCardExampleViewBinding::inflate,
setViewModel = ConceptCardExampleViewBinding::setSubtitledHtml)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,63 @@
package org.oppia.app.topic.conceptcard

import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
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 javax.inject.Inject

/** [ViewModel] for concept card, providing rich text and worked examples */
@FragmentScope
class ConceptCardViewModel @Inject constructor() : ViewModel() {
fun getDummyText() = "hello world"
}
class ConceptCardViewModel @Inject constructor(
private val topicController: TopicController,
private val logger: Logger
) : ViewModel() {

private lateinit var skillId: String

/** Live Data for concept card explanation */
val conceptCardLiveData: LiveData<ConceptCard> by lazy {
processConceptCardLiveData()
}

/** Live Data for concept card worked examples. */
val workedExamplesLiveData: LiveData<List<SubtitledHtml>> by lazy {
processWorkedExamplesLiveData()
}

/** Sets the value of skillId. Must be called before setting ViewModel to binding. */
fun setSkillId(id: String) {
skillId = id
}

private val conceptCardResultLiveData: LiveData<AsyncResult<ConceptCard>> by lazy {
topicController.getConceptCard(skillId)
}

private fun processConceptCardLiveData(): LiveData<ConceptCard> {
return Transformations.map(conceptCardResultLiveData, ::processConceptCardResult)
}

private fun processWorkedExamplesLiveData(): LiveData<List<SubtitledHtml>> {
return Transformations.map(conceptCardResultLiveData, ::processConceptCardWorkExamples)
}

private fun processConceptCardResult(conceptCardResult: AsyncResult<ConceptCard>): ConceptCard {
if (conceptCardResult.isFailure()) {
logger.e("ConceptCardFragment", "Failed to retrieve Concept Card: " + conceptCardResult.getErrorOrNull())
}
return conceptCardResult.getOrDefault(ConceptCard.getDefaultInstance())
}

private fun processConceptCardWorkExamples(conceptCardResult: AsyncResult<ConceptCard>): List<SubtitledHtml> {
if (conceptCardResult.isFailure()) {
logger.e("ConceptCardFragment", "Failed to retrieve Concept Card: " + conceptCardResult.getErrorOrNull())
}
return conceptCardResult.getOrDefault(ConceptCard.getDefaultInstance()).workedExampleList
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.oppia.app.topic.conceptcard.testing

import android.os.Bundle
import org.oppia.app.activity.InjectableAppCompatActivity
import javax.inject.Inject

/** Test Activity used for testing ConceptCardFragment */
class ConceptCardFragmentTestActivity : InjectableAppCompatActivity() {
jamesxu0 marked this conversation as resolved.
Show resolved Hide resolved

@Inject lateinit var conceptCardFragmentTestActivityController: ConceptCardFragmentTestActivityPresenter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityComponent.inject(this)
conceptCardFragmentTestActivityController.handleOnCreate()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.oppia.app.topic.conceptcard.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.topic.conceptcard.ConceptCardFragment
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)
frag.showNow(activity.supportFragmentManager, TAG_CONCEPT_CARD_DIALOG)
}
activity.open_dialog_2.setOnClickListener {
val frag = ConceptCardFragment.newInstance(TEST_SKILL_ID_2)
frag.showNow(activity.supportFragmentManager, TAG_CONCEPT_CARD_DIALOG)
}
}
}
22 changes: 22 additions & 0 deletions app/src/main/res/layout/concept_card_example_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="subtitledHtml"
type="org.oppia.app.model.SubtitledHtml" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:text="@{subtitledHtml.getHtml()}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
<import type="android.view.View"/>
<variable
name="viewModel"
type="org.oppia.app.topic.conceptcard.ConceptCardViewModel"/>
</data>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="match_parent"
android:layout_width="match_parent"

android:background="@color/colorConceptCardBackground">
<androidx.appcompat.widget.Toolbar
android:id="@+id/concept_card_toolbar"
Expand All @@ -28,13 +26,26 @@
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="@+id/rich_text_card"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:text="@{viewModel.getDummyText()}" />
android:orientation="vertical"
android:paddingTop="10dp">
<TextView
android:id="@+id/explanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:gravity="center"
android:visibility="@{viewModel.conceptCardLiveData.hasExplanation() ? View.VISIBLE : View.GONE}"
android:text="@{viewModel.conceptCardLiveData.explanation.getHtml()}" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/worked_examples"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:data="@{viewModel.workedExamplesLiveData}" />
</LinearLayout>
</ScrollView>

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
</layout>
14 changes: 14 additions & 0 deletions app/src/main/res/layout/concept_card_fragment_test_activity.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/open_dialog_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/open_dialog_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Loading