Skip to content

Commit

Permalink
Fix part of #137: Topic-play-tab low-fi (part-1) (Without expandable …
Browse files Browse the repository at this point in the history
…list) (#260)

* Topic-play-tab basic implementation

* Added test cases

* Nit changes

* Id rename

* StoryActivity introduced

* Nit changes

* New test cases added

* Nit changes
  • Loading branch information
rt4914 committed Oct 24, 2019
1 parent 1a391ed commit 4555085
Show file tree
Hide file tree
Showing 22 changed files with 514 additions and 33 deletions.
9 changes: 5 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@
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=".topic.conceptcard.testing.ConceptCardFragmentTestActivity" />
<activity android:name=".player.state.testing.StateFragmentTestActivity" />
<activity android:name=".player.exploration.ExplorationActivity" />
<activity android:name=".topic.questionplayer.QuestionPlayerActivity" />
<activity android:name=".topic.TopicActivity" />
<activity android:name=".player.audio.testing.AudioFragmentTestActivity" />
<activity android:name=".testing.InputInteractionViewTestActivity" />
<activity android:name=".profile.ProfileActivity" />
<activity android:name="org.oppia.app.home.HomeActivity">
<activity android:name=".story.StoryActivity"/>
<activity android:name=".home.HomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand All @@ -34,4 +35,4 @@
android:theme="@style/SplashScreenTheme" />
<activity android:name=".testing.BindableAdapterTestActivity" />
</application>
</manifest>
</manifest>
10 changes: 6 additions & 4 deletions app/src/main/java/org/oppia/app/activity/ActivityComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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.profile.ProfileActivity
import org.oppia.app.story.StoryActivity
import org.oppia.app.testing.BindableAdapterTestActivity
import org.oppia.app.topic.TopicActivity
import org.oppia.app.topic.questionplayer.QuestionPlayerActivity
Expand All @@ -27,13 +28,14 @@ interface ActivityComponent {

fun getFragmentComponentBuilderProvider(): Provider<FragmentComponent.Builder>

fun inject(audioFragmentTestActivity: AudioFragmentTestActivity)
fun inject(bindableAdapterTestActivity: BindableAdapterTestActivity)
fun inject(conceptCardFragmentTestActivity: ConceptCardFragmentTestActivity)
fun inject(explorationActivity: ExplorationActivity)
fun inject(homeActivity: HomeActivity)
fun inject(conceptCardFragmentTestActivity: ConceptCardFragmentTestActivity)
fun inject(profileActivity: ProfileActivity)
fun inject(questionPlayerActivity: QuestionPlayerActivity)
fun inject(topicActivity: TopicActivity)
fun inject(audioFragmentTestActivity: AudioFragmentTestActivity)
fun inject(stateFragmentTestActivity: StateFragmentTestActivity)
fun inject(profileActivity: ProfileActivity)
fun inject(storyActivity: StoryActivity)
fun inject(topicActivity: TopicActivity)
}
14 changes: 9 additions & 5 deletions app/src/main/java/org/oppia/app/fragment/FragmentComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.oppia.app.player.audio.AudioFragment
import org.oppia.app.profile.AddProfileFragment
import org.oppia.app.profile.AdminAuthFragment
import org.oppia.app.profile.ProfileChooserFragment
import org.oppia.app.story.StoryFragment
import org.oppia.app.testing.BindableAdapterTestFragment
import org.oppia.app.topic.TopicFragment
import org.oppia.app.topic.conceptcard.ConceptCardFragment
Expand All @@ -25,23 +26,26 @@ import org.oppia.app.topic.train.TopicTrainFragment
interface FragmentComponent {
@Subcomponent.Builder
interface Builder {
@BindsInstance fun setFragment(fragment: Fragment): Builder
@BindsInstance
fun setFragment(fragment: Fragment): Builder

fun build(): FragmentComponent
}

fun inject(addProfileFragment: AddProfileFragment)
fun inject(adminAuthFragment: AdminAuthFragment)
fun inject(audioFragment: AudioFragment)
fun inject(bindableAdapterTestFragment: BindableAdapterTestFragment)
fun inject(conceptCardFragment: ConceptCardFragment)
fun inject(explorationFragment: ExplorationFragment)
fun inject(homeFragment: HomeFragment)
fun inject(profileChooserFragment: ProfileChooserFragment)
fun inject(questionPlayerFragment: QuestionPlayerFragment)
fun inject(stateFragment: StateFragment)
fun inject(bindableAdapterTestFragment: BindableAdapterTestFragment)
fun inject(storyFragment: StoryFragment)
fun inject(topicFragment: TopicFragment)
fun inject(topicOverviewFragment: TopicOverviewFragment)
fun inject(topicPlayFragment: TopicPlayFragment)
fun inject(topicReviewFragment: TopicReviewFragment)
fun inject(topicTrainFragment: TopicTrainFragment)
fun inject(profileChooserFragment: ProfileChooserFragment)
fun inject(adminAuthFragment: AdminAuthFragment)
fun inject(addProfileFragment: AddProfileFragment)
}
29 changes: 29 additions & 0 deletions app/src/main/java/org/oppia/app/story/StoryActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.oppia.app.story

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

/** Activity for stories. */
class StoryActivity : InjectableAppCompatActivity() {
@Inject lateinit var storyActivityPresenter: StoryActivityPresenter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityComponent.inject(this)
storyActivityPresenter.handleOnCreate()
}

companion object {
const val STORY_ACTIVITY_STORY_ID_ARGUMENT_KEY = "StoryActivity.story_id"

/** Returns a new [Intent] to route to [StoryActivity] for a specified story ID. */
fun createStoryActivityIntent(context: Context, storyId: String): Intent {
val intent = Intent(context, StoryActivity::class.java)
intent.putExtra(STORY_ACTIVITY_STORY_ID_ARGUMENT_KEY, storyId)
return intent
}
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/org/oppia/app/story/StoryActivityPresenter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.oppia.app.story

import androidx.appcompat.app.AppCompatActivity
import org.oppia.app.R
import org.oppia.app.activity.ActivityScope
import javax.inject.Inject

/** The presenter for [StoryActivity]. */
@ActivityScope
class StoryActivityPresenter @Inject constructor(private val activity: AppCompatActivity) {
fun handleOnCreate() {
activity.setContentView(R.layout.story_activity)
if (getStoryFragment() == null) {
activity.supportFragmentManager.beginTransaction().add(
R.id.story_fragment_placeholder,
StoryFragment()
).commitNow()
}
}

private fun getStoryFragment(): StoryFragment? {
return activity.supportFragmentManager.findFragmentById(R.id.story_fragment_placeholder) as StoryFragment?
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/org/oppia/app/story/StoryFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.oppia.app.story

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.oppia.app.fragment.InjectableFragment
import javax.inject.Inject

/** Fragment that displays story with chapter list. */
class StoryFragment : InjectableFragment() {
@Inject
lateinit var storyFragmentPresenter: StoryFragmentPresenter

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

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return storyFragmentPresenter.handleCreateView(inflater, container)
}
}
23 changes: 23 additions & 0 deletions app/src/main/java/org/oppia/app/story/StoryFragmentPresenter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.oppia.app.story

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import org.oppia.app.databinding.StoryFragmentBinding
import org.oppia.app.fragment.FragmentScope
import javax.inject.Inject

/** The presenter for [StoryFragment]. */
@FragmentScope
class StoryFragmentPresenter @Inject constructor(
private val fragment: Fragment
) {
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
val binding = StoryFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding.let {
it.lifecycleOwner = fragment
}
return binding.root
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/org/oppia/app/topic/RouteToStoryListener.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.oppia.app.topic

/** Listener for when an [TopicActivity] should route to a [StoryActivity]. */
interface RouteToStoryListener {
fun routeToStory(storyId: String)
}
8 changes: 7 additions & 1 deletion app/src/main/java/org/oppia/app/topic/TopicActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package org.oppia.app.topic

import android.os.Bundle
import org.oppia.app.activity.InjectableAppCompatActivity
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 {
RouteToTopicPlayListener, RouteToStoryListener {
@Inject
lateinit var topicActivityPresenter: TopicActivityPresenter

Expand All @@ -22,6 +24,10 @@ class TopicActivity : InjectableAppCompatActivity(), RouteToQuestionPlayerListen
startActivity(QuestionPlayerActivity.createQuestionPlayerActivityIntent(this, skillIdList))
}

override fun routeToStory(storyId: String) {
startActivity(StoryActivity.createStoryActivityIntent(this, storyId))
}

override fun routeToTopicPlayFragment() {
// TODO(#135): Change to play tab in this function.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.oppia.app.databinding.TopicFragmentBinding
import org.oppia.app.fragment.FragmentScope
import javax.inject.Inject

/** The controller for [TopicFragment]. */
/** The presenter for [TopicFragment]. */
@FragmentScope
class TopicFragmentPresenter @Inject constructor(
private val fragment: Fragment
Expand Down
46 changes: 46 additions & 0 deletions app/src/main/java/org/oppia/app/topic/play/StorySummaryAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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
import org.oppia.app.model.StorySummary

// TODO(#216): Make use of generic data-binding-enabled RecyclerView adapter.
/** Adapter to bind StorySummary to [RecyclerView] inside [TopicPlayFragment]. */
class StorySummaryAdapter(
private var storyList: MutableList<StorySummary>,
private val storySummarySelector: StorySummarySelector
) :
RecyclerView.Adapter<StorySummaryAdapter.StorySummaryViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StorySummaryViewHolder {
val storySummaryListItemBinding = DataBindingUtil.inflate<TopicPlayStorySummaryBinding>(
LayoutInflater.from(parent.context),
R.layout.topic_play_story_summary, parent,
/* attachToRoot= */ false
)
return StorySummaryViewHolder(storySummaryListItemBinding)
}

override fun onBindViewHolder(storySummaryViewHolder: StorySummaryViewHolder, i: Int) {
storySummaryViewHolder.bind(storyList[i], i)
}

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

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)
binding.root.setOnClickListener {
storySummarySelector.selectedStorySummary(storySummary)
}
}
}
}
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.StorySummary

/** Interface to transfer the selected story summary to [TopicPlayFragmentPresenter]. */
interface StorySummarySelector {
fun selectedStorySummary(storySummary: StorySummary)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,73 @@ package org.oppia.app.topic.play
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.Transformations
import org.oppia.app.databinding.TopicPlayFragmentBinding
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.model.StorySummary
import org.oppia.app.model.Topic
import org.oppia.app.topic.RouteToStoryListener
import org.oppia.domain.topic.TEST_TOPIC_ID_0
import org.oppia.domain.topic.TopicController
import org.oppia.util.data.AsyncResult
import org.oppia.util.logging.Logger
import javax.inject.Inject

/** The presenter for [TopicPlayFragment]. */
@FragmentScope
class TopicPlayFragmentPresenter @Inject constructor(
private val fragment: Fragment
) {
activity: AppCompatActivity,
private val fragment: Fragment,
private val logger: Logger,
private val topicController: TopicController
) : StorySummarySelector {

private val routeToStoryListener = activity as RouteToStoryListener

private lateinit var binding: TopicPlayFragmentBinding

fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
val binding = TopicPlayFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding = TopicPlayFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)

binding.let {
it.lifecycleOwner = fragment
}
subscribeToTopicLiveData()
return binding.root
}

private val topicLiveData: LiveData<Topic> by lazy { getTopicList() }

// TODO(#135): Get this topic-id or get storyList from [StoryFragment].
private val topicResultLiveData: LiveData<AsyncResult<Topic>> by lazy {
topicController.getTopic(TEST_TOPIC_ID_0)
}

private fun subscribeToTopicLiveData() {
topicLiveData.observe(fragment, Observer<Topic> {
val storySummaryAdapter = StorySummaryAdapter(it.storyList, this as StorySummarySelector)
binding.storySummaryRecyclerView.apply {
adapter = storySummaryAdapter
}
})
}

private fun getTopicList(): LiveData<Topic> {
return Transformations.map(topicResultLiveData, ::processTopicResult)
}

private fun processTopicResult(topic: AsyncResult<Topic>): Topic {
if (topic.isFailure()) {
logger.e("TopicPlayFragment", "Failed to retrieve topic", topic.getErrorOrNull()!!)
}
return topic.getOrDefault(Topic.getDefaultInstance())
}

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

0 comments on commit 4555085

Please sign in to comment.