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

Fix part of #138: Topic train fragment Low-fi UI (Part 3) #203

Merged
merged 19 commits into from
Oct 8, 2019
14 changes: 8 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http:https://schemas.android.com/apk/res/android"
package="org.oppia.app">
<uses-permission android:name="android.permission.INTERNET" />
<manifest xmlns:android="http:https://schemas.android.com/apk/res/android"
package="org.oppia.app">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".application.OppiaApplication"
android:allowBackup="true"
Expand All @@ -12,7 +11,9 @@
android:supportsRtl="true"
android:theme="@style/OppiaTheme">
<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 All @@ -24,8 +25,9 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name=".splash.SplashActivity"
android:theme="@style/SplashScreenTheme">
<activity
android:name=".splash.SplashActivity"
android:theme="@style/SplashScreenTheme">
</activity>
<activity android:name=".testing.BindableAdapterTestActivity"/>
</application>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.oppia.app.player.exploration.ExplorationActivity
import org.oppia.app.player.state.testing.StateFragmentTestActivity
import org.oppia.app.testing.BindableAdapterTestActivity
import org.oppia.app.topic.TopicActivity
import org.oppia.app.topic.questionplayer.QuestionPlayerActivity
import javax.inject.Provider

/** Root subcomponent for all activities. */
Expand All @@ -23,9 +24,10 @@ interface ActivityComponent {

fun getFragmentComponentBuilderProvider(): Provider<FragmentComponent.Builder>

fun inject(bindableAdapterTestActivity: BindableAdapterTestActivity)
fun inject(explorationActivity: ExplorationActivity)
fun inject(homeActivity: HomeActivity)
fun inject(bindableAdapterTestActivity: BindableAdapterTestActivity)
fun inject(questionPlayerActivity: QuestionPlayerActivity)
fun inject(topicActivity: TopicActivity)
fun inject(stateFragmentTestActivity: StateFragmentTestActivity)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.oppia.app.topic.TopicFragment
import org.oppia.app.topic.conceptcard.ConceptCardFragment
import org.oppia.app.topic.overview.TopicOverviewFragment
import org.oppia.app.topic.play.TopicPlayFragment
import org.oppia.app.topic.questionplayer.QuestionPlayerFragment
import org.oppia.app.topic.review.TopicReviewFragment
import org.oppia.app.topic.train.TopicTrainFragment

Expand All @@ -29,6 +30,7 @@ interface FragmentComponent {
fun inject(conceptCardFragment: ConceptCardFragment)
fun inject(explorationFragment: ExplorationFragment)
fun inject(homeFragment: HomeFragment)
fun inject(questionPlayerFragment: QuestionPlayerFragment)
fun inject(stateFragment: StateFragment)
fun inject(bindableAdapterTestFragment: BindableAdapterTestFragment)
fun inject(topicFragment: TopicFragment)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.oppia.app.topic

/** Listener for when an activity should route to a Question Player. */
interface RouteToQuestionPlayerListener {
fun routeToQuestionPlayer(skillIdList: ArrayList<String>)
}
7 changes: 6 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,15 +2,20 @@ package org.oppia.app.topic

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

/** The activity for tabs in Topic. */
class TopicActivity : InjectableAppCompatActivity() {
class TopicActivity : InjectableAppCompatActivity(), RouteToQuestionPlayerListener {
@Inject lateinit var topicActivityPresenter: TopicActivityPresenter

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

override fun routeToQuestionPlayer(skillIdList: ArrayList<String>) {
startActivity(QuestionPlayerActivity.createQuestionPlayerActivityIntent(this, skillIdList))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.oppia.app.topic.questionplayer

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

const val QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY = "QuestionPlayerActivity.skill_id_list"

/** Activity for QuestionPlayer in train mode. */
class QuestionPlayerActivity : InjectableAppCompatActivity() {
@Inject
lateinit var questionPlayerActivityPresenter: QuestionPlayerActivityPresenter

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

companion object {
// TODO(#159): Use this skillList from TopicTrainFragment to fetch questions and start train mode.
/** Returns a new [Intent] to route to [QuestionPlayerActivity] for a specified skill ID list. */
fun createQuestionPlayerActivityIntent(context: Context, skillIdList: ArrayList<String>): Intent {
val intent = Intent(context, QuestionPlayerActivity::class.java)
intent.putExtra(QUESTION_PLAYER_ACTIVITY_SKILL_ID_LIST_ARGUMENT_KEY, skillIdList)
return intent
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.oppia.app.topic.questionplayer

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

/** The presenter for [QuestionPlayerActivity]. */
@ActivityScope
class QuestionPlayerActivityPresenter @Inject constructor(private val activity: AppCompatActivity) {
fun handleOnCreate() {
activity.setContentView(R.layout.question_player_activity)
if (getQuestionPlayerFragment() == null) {
activity.supportFragmentManager.beginTransaction().add(
R.id.question_player_fragment_placeholder,
QuestionPlayerFragment()
).commitNow()
}
}

private fun getQuestionPlayerFragment(): QuestionPlayerFragment? {
return activity.supportFragmentManager.findFragmentById(R.id.question_player_fragment_placeholder) as QuestionPlayerFragment?
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.oppia.app.topic.questionplayer

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 contains all questions in Question Player. */
class QuestionPlayerFragment: InjectableFragment(){
@Inject
lateinit var questionPlayerFragmentPresenter: QuestionPlayerFragmentPresenter

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

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return questionPlayerFragmentPresenter.handleCreateView(inflater, container)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.oppia.app.topic.questionplayer

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

/** The presenter for [QuestionPlayerFragment]. */
@FragmentScope
class QuestionPlayerFragmentPresenter @Inject constructor(
private val fragment: Fragment
) {
fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
val binding = QuestionPlayerFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding.let {
it.lifecycleOwner = fragment
}
return binding.root
}
}
Original file line number Diff line number Diff line change
@@ -1,51 +1,58 @@
package org.oppia.app.topic.train;
package org.oppia.app.topic.train

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.databinding.library.baseAdapters.BR
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.topic_train_skill_view.view.*
import org.oppia.app.R
import org.oppia.app.databinding.TopicTrainSkillViewBinding
import org.oppia.app.model.SkillSummary

// TODO(#216): Make use of generic data-binding-enabled RecyclerView adapter.
/** Adapter to bind skills to [RecyclerView] inside [TopicTrainFragment]. */
class SkillSelectionAdapter(
private val skillList: List<String>,
private val skillSelector: SkillSelector
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding =
DataBindingUtil.inflate<TopicTrainSkillViewBinding>(
inflater,
R.layout.topic_train_skill_view,
parent,
/* attachToParent= */false
)
return SkillViewHolder(binding)
class SkillSelectionAdapter(private val skillSelector: SkillSelector) :
RecyclerView.Adapter<SkillSelectionAdapter.SkillViewHolder>() {

private var skillList: List<SkillSummary> = ArrayList()
private var selectedSkillIdList: ArrayList<String> = ArrayList()

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SkillViewHolder {
val skillListItemBinding = DataBindingUtil.inflate<TopicTrainSkillViewBinding>(
LayoutInflater.from(parent.context),
R.layout.topic_train_skill_view, parent,
/* attachToRoot= */ false
)
return SkillViewHolder(skillListItemBinding)
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as SkillViewHolder).bind(skillList[position], position)
override fun onBindViewHolder(skillViewHolder: SkillViewHolder, i: Int) {
skillViewHolder.bind(skillList[i], i)
}

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

private inner class SkillViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) {
internal fun bind(rawString: String?, position: Int) {
binding.setVariable(BR.skill, rawString)
fun setSkillList(skillList: List<SkillSummary>) {
this.skillList = skillList
notifyDataSetChanged()
}

fun setSelectedSkillList(skillIdList: ArrayList<String>) {
selectedSkillIdList = skillIdList
}

inner class SkillViewHolder(val binding: TopicTrainSkillViewBinding) : RecyclerView.ViewHolder(binding.root) {
internal fun bind(skill: SkillSummary, position: Int) {
binding.setVariable(BR.isChecked, selectedSkillIdList.contains(skill.skillId))
binding.setVariable(BR.skill, skill)
binding.root.skill_check_box.setOnCheckedChangeListener { buttonView, isChecked ->
val skill = skillList[position]
if (isChecked) {
skillSelector.skillSelected(skill)
skillSelector.skillSelected(skill.skillId)
} else {
skillSelector.skillUnselected(skill)
skillSelector.skillUnselected(skill.skillId)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package org.oppia.app.topic.train
/** Interface to update the selectedSkillList in [TopicTrainFragmentPresenter]. */
interface SkillSelector {
/** This skill will get added to selectedSkillList in [TopicTrainFragmentPresenter]. */
fun skillSelected(skill: String)
fun skillSelected(skillId: String)

/** This skill will get removed from selectedSkillList in [TopicTrainFragmentPresenter]. */
fun skillUnselected(skill: String)
fun skillUnselected(skillId: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import android.view.ViewGroup
import org.oppia.app.fragment.InjectableFragment
import javax.inject.Inject

/** Fragment that contains skills for topic train mode. */
private const val KEY_SKILL_ID_LIST = "SKILL_ID_LIST"

/** Fragment that displays skills for topic train mode. */
class TopicTrainFragment : InjectableFragment() {
@Inject
lateinit var topicTrainFragmentPresenter: TopicTrainFragmentPresenter
Expand All @@ -19,6 +21,15 @@ class TopicTrainFragment : InjectableFragment() {
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return topicTrainFragmentPresenter.handleCreateView(inflater, container)
var selectedIdList = ArrayList<String>()
if (savedInstanceState != null) {
selectedIdList = savedInstanceState.getStringArrayList(KEY_SKILL_ID_LIST)
}
return topicTrainFragmentPresenter.handleCreateView(inflater, container, selectedIdList)
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putStringArrayList(KEY_SKILL_ID_LIST, topicTrainFragmentPresenter.selectedSkillIdList)
}
}
Loading