Skip to content

Commit

Permalink
Fix #137: Topic play low fi part 3 with nested RecyclerView (#271)
Browse files Browse the repository at this point in the history
* Added story-progress and test cases

* RV inside RV

* Test case modified

* LV implementation

* Finalised RecyclerView

* Visbility contol

* Play exploration connected

* Single list item expansaion

* Initial test cases

* Nit change

* Test cases added

* Configuration change support

* Nit changes

* Nit changes

* Nit changes

* Nit changes

* Added issue number

* Nit change
  • Loading branch information
rt4914 committed Oct 31, 2019
1 parent 95bef9f commit 02b606d
Show file tree
Hide file tree
Showing 14 changed files with 497 additions and 26 deletions.
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<activity android:name=".player.audio.testing.AudioFragmentTestActivity" />
<activity android:name=".testing.InputInteractionViewTestActivity" />
<activity android:name=".profile.ProfileActivity" />
<activity android:name=".story.StoryActivity"/>
<activity android:name=".story.StoryActivity" />
<activity android:name=".home.HomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
10 changes: 8 additions & 2 deletions app/src/main/java/org/oppia/app/topic/TopicActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ package org.oppia.app.topic

import android.os.Bundle
import org.oppia.app.activity.InjectableAppCompatActivity
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.questionplayer.QuestionPlayerActivity
import javax.inject.Inject

/** The activity for tabs in Topic. */

class TopicActivity : InjectableAppCompatActivity(), RouteToQuestionPlayerListener, RouteToConceptCardListener,
RouteToTopicPlayListener, RouteToStoryListener {
RouteToTopicPlayListener, RouteToStoryListener, RouteToExplorationListener {

@Inject
lateinit var topicActivityPresenter: TopicActivityPresenter

Expand Down Expand Up @@ -39,6 +41,10 @@ class TopicActivity : InjectableAppCompatActivity(), RouteToQuestionPlayerListen
}
}

override fun routeToExploration(explorationId: String) {
startActivity(ExplorationActivity.createExplorationActivityIntent(this, explorationId))
}

private fun getConceptCardFragment(): ConceptCardFragment? {
return supportFragmentManager.findFragmentByTag(TAG_CONCEPT_CARD_DIALOG) as ConceptCardFragment?
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.oppia.app.topic.play

import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.oppia.app.R
import androidx.databinding.DataBindingUtil
import android.view.LayoutInflater
import org.oppia.app.databinding.PlayChapterViewBinding
import org.oppia.app.model.ChapterSummary

// TODO(#216): Make use of generic data-binding-enabled RecyclerView adapter.

/** Adapter to bind ChapterSummary to [RecyclerView] inside [TopicPlayFragment]. */
class ChapterSummaryAdapter(
private val chapterList: List<ChapterSummary>,
private val chapterSummarySelector: ChapterSummarySelector
) :
RecyclerView.Adapter<ChapterSummaryAdapter.ChapterSummaryViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChapterSummaryViewHolder {
val chapterSummaryListItemBinding = DataBindingUtil.inflate<PlayChapterViewBinding>(
LayoutInflater.from(parent.context),
R.layout.play_chapter_view, parent,
/* attachToRoot= */ false
)
return ChapterSummaryViewHolder(chapterSummaryListItemBinding)
}

override fun getItemCount(): Int {
return chapterList.size
}

override fun onBindViewHolder(chapterSummaryViewHolder: ChapterSummaryViewHolder, position: Int) {
chapterSummaryViewHolder.bind(chapterList[position])
}

inner class ChapterSummaryViewHolder(private val binding: PlayChapterViewBinding) :
RecyclerView.ViewHolder(binding.root) {
internal fun bind(chapterSummary: ChapterSummary) {
// TODO(#286): Fix the flickering behaviour of chapter-completion image.
binding.chapterSummary = chapterSummary
binding.chapterName.setOnClickListener {
chapterSummarySelector.selectChapterSummary(chapterSummary)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.oppia.app.topic.play

import org.oppia.app.model.ChapterSummary

/** Interface to transfer the selected chapter summary to [TopicPlayFragmentPresenter]. */
interface ChapterSummarySelector {
fun selectChapterSummary(chapterSummary: ChapterSummary)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.oppia.app.topic.play

/**
* Interface to keep track of story-index for which ChapterList is visible/expanded.
* This mainly helps to maintain the state during configuration change.
*/
interface ExpandedChapterListIndexListener {
fun onExpandListIconClicked(index: Int?)
}
39 changes: 33 additions & 6 deletions app/src/main/java/org/oppia/app/topic/play/StorySummaryAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package org.oppia.app.topic.play
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.library.baseAdapters.BR
import androidx.recyclerview.widget.RecyclerView
import org.oppia.app.R
import org.oppia.app.databinding.TopicPlayStorySummaryBinding
Expand All @@ -16,7 +15,10 @@ import org.oppia.app.model.StorySummary
/** Adapter to bind StorySummary to [RecyclerView] inside [TopicPlayFragment]. */
class StorySummaryAdapter(
private var storyList: MutableList<StorySummary>,
private val storySummarySelector: StorySummarySelector
private val chapterSummarySelector: ChapterSummarySelector,
private val storySummarySelector: StorySummarySelector,
private val expandedChapterListIndexListener: ExpandedChapterListIndexListener,
private var currentExpandedChapterListIndex: Int?
) :
RecyclerView.Adapter<StorySummaryAdapter.StorySummaryViewHolder>() {

Expand All @@ -39,8 +41,13 @@ class StorySummaryAdapter(

inner class StorySummaryViewHolder(private val binding: TopicPlayStorySummaryBinding) :
RecyclerView.ViewHolder(binding.root) {
internal fun bind(storySummary: StorySummary, @Suppress("UNUSED_PARAMETER") position: Int) {
binding.setVariable(BR.storySummary, storySummary)
internal fun bind(storySummary: StorySummary, position: Int) {
var isChapterListVisible = false
if(currentExpandedChapterListIndex!=null){
isChapterListVisible = currentExpandedChapterListIndex!! == position
}
binding.isListExpanded = isChapterListVisible
binding.storySummary = storySummary

val totalChapterCount = storySummary.chapterCount
val chapterSummaries = storySummary.chapterList
Expand All @@ -51,11 +58,31 @@ class StorySummaryAdapter(
}
.size

val chapterList = storySummary.chapterList
binding.chapterRecyclerView.adapter = ChapterSummaryAdapter(chapterList, chapterSummarySelector)

val storyProgressPercentage: Int = (completedChapterCount * 100) / totalChapterCount
binding.setVariable(BR.storyProgressPercentage, storyProgressPercentage)

binding.storyPercentage = storyProgressPercentage

binding.storyNameTextView.setOnClickListener {
storySummarySelector.selectedStorySummary(storySummary)
storySummarySelector.selectStorySummary(storySummary)
}

binding.chapterListViewControl.setOnClickListener {
val previousIndex: Int? = currentExpandedChapterListIndex
currentExpandedChapterListIndex = if (currentExpandedChapterListIndex!=null && currentExpandedChapterListIndex == position) {
null
} else {
position
}
expandedChapterListIndexListener.onExpandListIconClicked(currentExpandedChapterListIndex)
if(previousIndex!=null) {
notifyItemChanged(previousIndex)
}
if(currentExpandedChapterListIndex!=null) {
notifyItemChanged(currentExpandedChapterListIndex!!)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import org.oppia.app.model.StorySummary

/** Interface to transfer the selected story summary to [TopicPlayFragmentPresenter]. */
interface StorySummarySelector {
fun selectedStorySummary(storySummary: StorySummary)
fun selectStorySummary(storySummary: StorySummary)
}
25 changes: 23 additions & 2 deletions app/src/main/java/org/oppia/app/topic/play/TopicPlayFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,38 @@ import android.view.ViewGroup
import org.oppia.app.fragment.InjectableFragment
import javax.inject.Inject

private const val KEY_CURRENT_EXPANDED_LIST_INDEX = "CURRENT_EXPANDED_LIST_INDEX"

/** Fragment that contains subtopic list for play mode. */
class TopicPlayFragment : InjectableFragment() {
class TopicPlayFragment : InjectableFragment(), ExpandedChapterListIndexListener {
@Inject
lateinit var topicPlayFragmentPresenter: TopicPlayFragmentPresenter

private var currentExpandedChapterListIndex: Int? = null

override fun onAttach(context: Context?) {
super.onAttach(context)
fragmentComponent.inject(this)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return topicPlayFragmentPresenter.handleCreateView(inflater, container)
if (savedInstanceState != null) {
currentExpandedChapterListIndex = savedInstanceState.getInt(KEY_CURRENT_EXPANDED_LIST_INDEX)
}
return topicPlayFragmentPresenter.handleCreateView(
inflater,
container,
currentExpandedChapterListIndex,
this as ExpandedChapterListIndexListener
)
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(KEY_CURRENT_EXPANDED_LIST_INDEX, currentExpandedChapterListIndex!!)
}

override fun onExpandListIconClicked(index: Int?) {
currentExpandedChapterListIndex = index
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.Transformations
import org.oppia.app.databinding.TopicPlayFragmentBinding
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.home.RouteToExplorationListener
import org.oppia.app.model.ChapterSummary
import org.oppia.app.model.StorySummary
import org.oppia.app.model.Topic
import org.oppia.app.topic.RouteToStoryListener
import org.oppia.domain.exploration.ExplorationDataController
import org.oppia.domain.topic.TEST_TOPIC_ID_0
import org.oppia.domain.topic.TopicController
import org.oppia.util.data.AsyncResult
Expand All @@ -25,16 +28,28 @@ class TopicPlayFragmentPresenter @Inject constructor(
activity: AppCompatActivity,
private val fragment: Fragment,
private val logger: Logger,
private val explorationDataController: ExplorationDataController,
private val topicController: TopicController
) : StorySummarySelector {

) : StorySummarySelector, ChapterSummarySelector {
private val routeToExplorationListener = activity as RouteToExplorationListener
private val routeToStoryListener = activity as RouteToStoryListener

private var currentExpandedChapterListIndex: Int? = null

private lateinit var binding: TopicPlayFragmentBinding

fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
binding = TopicPlayFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
private lateinit var expandedChapterListIndexListener: ExpandedChapterListIndexListener

fun handleCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
currentExpandedChapterListIndex: Int?,
expandedChapterListIndexListener: ExpandedChapterListIndexListener
): View? {
this.currentExpandedChapterListIndex = currentExpandedChapterListIndex
this.expandedChapterListIndexListener = expandedChapterListIndexListener

binding = TopicPlayFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding.let {
it.lifecycleOwner = fragment
}
Expand All @@ -51,7 +66,14 @@ class TopicPlayFragmentPresenter @Inject constructor(

private fun subscribeToTopicLiveData() {
topicLiveData.observe(fragment, Observer<Topic> {
val storySummaryAdapter = StorySummaryAdapter(it.storyList, this as StorySummarySelector)
val storySummaryAdapter =
StorySummaryAdapter(
it.storyList,
this as ChapterSummarySelector,
this as StorySummarySelector,
expandedChapterListIndexListener,
currentExpandedChapterListIndex
)
binding.storySummaryRecyclerView.apply {
adapter = storySummaryAdapter
}
Expand All @@ -69,7 +91,26 @@ class TopicPlayFragmentPresenter @Inject constructor(
return topic.getOrDefault(Topic.getDefaultInstance())
}

override fun selectedStorySummary(storySummary: StorySummary) {
override fun selectStorySummary(storySummary: StorySummary) {
routeToStoryListener.routeToStory(storySummary.storyId)
}

override fun selectChapterSummary(chapterSummary: ChapterSummary) {
playExploration(chapterSummary.explorationId)
}

private fun playExploration(explorationId: String) {
explorationDataController.startPlayingExploration(
explorationId
).observe(fragment, Observer<AsyncResult<Any?>> { result ->
when {
result.isPending() -> logger.d("TopicPlayFragment", "Loading exploration")
result.isFailure() -> logger.e("TopicPlayFragment", "Failed to load exploration", result.getErrorOrNull()!!)
else -> {
logger.d("TopicPlayFragment", "Successfully loaded exploration")
routeToExplorationListener.routeToExploration(explorationId)
}
}
})
}
}
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_check_24dp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http:https://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#019489"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
</vector>
Loading

0 comments on commit 02b606d

Please sign in to comment.