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 #153/#154 : MultipleChoice/ItemSelection Interaction View #188 #196

Closed
wants to merge 39 commits into from
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e19af2b
Introduce first pass interface for ExplorationProgressController.
BenHenning Sep 25, 2019
43766b0
Fill in the stubbed logic for ExplorationProgressController. Still no
BenHenning Sep 26, 2019
2bdc5e8
added interactions
veena14cs Sep 26, 2019
b4ddf76
Update ContentInteractionFragmentPresenter.kt
veena14cs Sep 26, 2019
03c70b0
Update ContentInteractionFragmentPresenter.kt
veena14cs Sep 26, 2019
ad19fb7
updated urlImageParser file name
veena14cs Sep 27, 2019
c8eaa66
Fix lateinit issue in ExplorationProgressController due to wrongly or…
BenHenning Sep 27, 2019
bc710cd
created customviews. moved implementation to statefragment
veena14cs Sep 27, 2019
e43a664
removed usused layout files
veena14cs Sep 27, 2019
dd973f6
fixed const values, fixed constructor
veena14cs Sep 27, 2019
29951f0
removed extra spaces
veena14cs Sep 27, 2019
8c32704
Update StateFragmentPresenter.kt
veena14cs Sep 27, 2019
80fc9f7
updated todo
veena14cs Sep 27, 2019
70fceea
removed initviews method
veena14cs Sep 27, 2019
fc8ad8e
reverted changes
veena14cs Sep 27, 2019
a0eb3ce
Fix a variaty of issues in the exp progress controller, properly hook…
BenHenning Sep 29, 2019
1ea9d01
Merge branch 'develop' into introduce-exploration-progress-controller
BenHenning Sep 29, 2019
db17b25
Update UrlImageParser.kt
veena14cs Sep 30, 2019
6fc9814
Update state_fragment.xml
veena14cs Sep 30, 2019
f43f76d
Update StateFragmentPresenterTest.kt
veena14cs Sep 30, 2019
81814f9
added test cases
veena14cs Sep 30, 2019
c6cbcbf
rmoved debug code
veena14cs Sep 30, 2019
4c15db4
fixed all issues
veena14cs Sep 30, 2019
42fb6a9
Update UrlImageParser.kt
veena14cs Sep 30, 2019
db155ef
added indentation
veena14cs Sep 30, 2019
63db6b4
Update state_fragment.xml
veena14cs Sep 30, 2019
09495c7
removed spaces
veena14cs Sep 30, 2019
9ffebad
Update UrlImageParser.kt
veena14cs Sep 30, 2019
41141b6
Created a separate ExplorationRetriever, hooked up
BenHenning Oct 1, 2019
ccbac0e
Merge branch 'develop' into introduce-exploration-progress-controller
BenHenning Oct 1, 2019
9022a62
Removed custom views, used recyclerview for interactions
veena14cs Oct 1, 2019
5f6aa89
worked on testcases
veena14cs Oct 1, 2019
4f20fba
Update StateFragmentTest.kt
veena14cs Oct 1, 2019
fa2369b
reverted debug code, moved htmlparser to utility
veena14cs Oct 1, 2019
0d9108a
Update codeStyleConfig.xml
veena14cs Oct 1, 2019
54f1b65
Merge branch 'introduce-exploration-progress-controller' into explora…
veena14cs Oct 1, 2019
84360c4
working on fetching data from exploration
veena14cs Oct 1, 2019
38c843e
working on fetching data from exploration
veena14cs Oct 3, 2019
c990330
revert manifest code
veena14cs Oct 3, 2019
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
69 changes: 69 additions & 0 deletions app/src/main/java/org/oppia/app/player/state/CustomCheckbox.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.oppia.app.player.state

import android.content.Context
import android.text.Html
import android.text.Spannable
import android.text.Spanned
import android.view.View
import android.widget.CheckBox
import android.widget.CompoundButton
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat
import org.oppia.app.R
import org.oppia.util.data.UrlImageParser
import android.content.res.ColorStateList
import android.os.Build
import android.view.Gravity

// TODO(#163): Move this to a StateCardFragment Low-fi PR.
/** Custom Checkbox for MultipleSelectionInputInteractionView. */
class CustomCheckbox(context: Context, private val optionContents: String) : CheckBox(context) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it valid to have custom parameters in a view constructor? Since Android can construct these, I'd expect that if we want to pass in custom data we should instead use a setter method on the view.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of CustomCheckbox and CustomRadioButton maybe instead: HtmlCheckbox and HtmlRadioButton might be a bit clearer as to what these views are doing & how they should be used.


// Update default attributes of ItemSelectionInputInteractionView here.
init {
val paddingPixel = 2
val density = resources.displayMetrics.density
val paddingDp = (paddingPixel * density).toInt()
gravity = Gravity.LEFT
setTextColor(ContextCompat.getColor(context, R.color.oppiaDarkBlue))
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setButtonTintList(
ColorStateList(
arrayOf(
intArrayOf(android.R.attr.state_enabled)
),
intArrayOf(ContextCompat.getColor(context, R.color.oppiaDarkBlue))
)
);
}
setHighlightColor(ContextCompat.getColor(context, R.color.oppiaDarkBlue))
textSize = 16f
setPadding(paddingDp, paddingDp, paddingDp, paddingDp)
id = View.generateViewId()
text = convertHtmlToString(optionContents, rootView).toString()
setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
val msg = "You have " + (if (isChecked) "checked" else "unchecked") + " this Check it Checkbox."
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
})
}

fun convertHtmlToString(rawResponse: Any?, rdbtn: View): Spanned {
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
var htmlContent = rawResponse as String;
var result: Spanned
if (htmlContent!!.contains(CUSTOM_TAG)) {
htmlContent = htmlContent.replace(CUSTOM_TAG, HTML_TAG, false);
htmlContent = htmlContent.replace(CUSTOM_ATTRIBUTE, HTML_ATTRIBUTE, false);
htmlContent = htmlContent.replace(""", "")
}
var imageGetter = UrlImageParser(rdbtn as TextView, context)
val html: Spannable
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
html = Html.fromHtml(htmlContent, Html.FROM_HTML_MODE_LEGACY, imageGetter, null) as Spannable
} else {
html = Html.fromHtml(htmlContent, imageGetter, null) as Spannable
}
result = html
return result;
}
}
67 changes: 67 additions & 0 deletions app/src/main/java/org/oppia/app/player/state/CustomRadioButton.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.oppia.app.player.state

import android.content.Context
import android.content.res.ColorStateList
import android.os.Build
import android.text.Html
import android.text.Spannable
import android.text.Spanned
import android.util.Log
import android.view.Gravity
import android.view.View
import android.widget.RadioButton
import android.widget.TextView
import androidx.core.content.ContextCompat
import org.oppia.app.R
import org.oppia.util.data.UrlImageParser

const val CUSTOM_TAG = "oppia-noninteractive-image"
const val HTML_TAG = "img"
const val CUSTOM_ATTRIBUTE = "filepath-with-value"
const val HTML_ATTRIBUTE = "src"

// TODO(#163): Move this to a StateCardFragment Low-fi PR.
/** Custom Checkbox for MultipleSelectionInputInteractionView. */
class CustomRadioButton(context: Context,private val optionContents: String) : RadioButton(context) {

// Update default attributes of ItemSelectionInputInteractionView here.
init {
val paddingPixel = 2
val density = resources.displayMetrics.density
val paddingDp = (paddingPixel * density).toInt()
gravity = Gravity.LEFT
setTextColor(ContextCompat.getColor(context, R.color.oppiaDarkBlue))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setButtonTintList(ColorStateList(
arrayOf(
intArrayOf(android.R.attr.state_enabled)
),
intArrayOf(ContextCompat.getColor(context, R.color.oppiaDarkBlue))
));
}
setHighlightColor(ContextCompat.getColor(context, R.color.oppiaDarkBlue))
textSize = 16f
setPadding(paddingDp, paddingDp, paddingDp, paddingDp)
id = View.generateViewId()
text = convertHtmlToString(optionContents, rootView).toString()
}

fun convertHtmlToString(rawResponse: Any?, rdbtn: View): Spanned {
var htmlContent = rawResponse as String;
var result: Spanned
if (htmlContent!!.contains(CUSTOM_TAG)) {
htmlContent = htmlContent.replace(CUSTOM_TAG, HTML_TAG, false);
htmlContent = htmlContent.replace(CUSTOM_ATTRIBUTE, HTML_ATTRIBUTE, false);
htmlContent = htmlContent.replace(""", "")
}
var imageGetter = UrlImageParser(rdbtn as TextView, context)
val html: Spannable
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
html = Html.fromHtml(htmlContent, Html.FROM_HTML_MODE_LEGACY, imageGetter, null) as Spannable
} else {
html = Html.fromHtml(htmlContent, imageGetter, null) as Spannable
}
result = html
return result;
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
package org.oppia.app.player.state

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.state_fragment.view.*
import org.oppia.app.application.ApplicationContext
import org.oppia.app.databinding.StateFragmentBinding
import org.oppia.app.fragment.FragmentScope
import org.oppia.app.viewmodel.ViewModelProvider
import org.oppia.data.backends.gae.model.GaeCustomizationArgs
import javax.inject.Inject

/** The presenter for [StateFragment]. */
@FragmentScope
class StateFragmentPresenter @Inject constructor(
@ApplicationContext private val context: Context,
private val fragment: Fragment,
private val viewModelProvider: ViewModelProvider<StateViewModel>
) {

var gaeCustomizationArgsMap = HashMap<String, GaeCustomizationArgs>()
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
var interactionInstanceId: String? = null

fun handleCreateView(inflater: LayoutInflater, container: ViewGroup?): View? {
val binding = StateFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
val binding = StateFragmentBinding.inflate(inflater, container, /* attachToRoot= */ false)
binding.let {
it.stateFragment = fragment as StateFragment
it.viewModel = getStateViewModel()
}
createDummyData()
showInputInteractions(binding)
return binding.root
}

Expand All @@ -31,4 +46,62 @@ class StateFragmentPresenter @Inject constructor(
fun setAudioFragmentVisible(isVisible: Boolean) {
getStateViewModel().setAudioFragmentVisible(isVisible)
}

private fun createDummyData() {
interactionInstanceId = "ItemSelectionInput"
var sampleData: GaeCustomizationArgs =
GaeCustomizationArgs(true, "<p>The numerator.</p>, <p>The denominator.</p>, <p>I can't remember!</p>]")
gaeCustomizationArgsMap?.put("choices", sampleData)
}

private fun showInputInteractions(binding: StateFragmentBinding) {
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
val gaeCustomizationArgs: Any? = gaeCustomizationArgsMap!!.get("choices")?.value
if (interactionInstanceId.equals("MultipleChoiceInput")) {
val gaeCustomArgsInString: String = gaeCustomizationArgs.toString().replace("[", "").replace("]", "")
var items = gaeCustomArgsInString.split(",").toTypedArray()
addRadioButtons(items,binding)
} else if (interactionInstanceId.equals("ItemSelectionInput") || interactionInstanceId.equals("SingleChoiceInput")) {
val gaeCustomArgsInString: String = gaeCustomizationArgs.toString().replace("[", "").replace("]", "")
var items = gaeCustomArgsInString.split(",").toTypedArray()
addCheckbox(items,binding)
} else {
//Do no show any view
}
}

fun addCheckbox(optionsArray: Array<String>, binding: StateFragmentBinding) {
for (row in 0..0) {
for (i in 0..optionsArray.size - 1) {
val cb = CustomCheckbox(context, optionsArray[i])
binding.root.interactionContainer.addView(cb)
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

fun addRadioButtons(
optionsArray: Array<String>,
binding: StateFragmentBinding
) {
for (row in 0..0) {
val rg = RadioGroup(context)
rg.orientation = LinearLayout.VERTICAL
for (i in 0..optionsArray.size - 1) {
val rdbtn = CustomRadioButton(context, optionsArray[i])
rg.setOnCheckedChangeListener(object : RadioGroup.OnCheckedChangeListener {
override fun onCheckedChanged(group: RadioGroup, checkedId: Int) {
for (i in 0 until group.childCount) {
val btn = group.getChildAt(i) as RadioButton
if (btn.id == checkedId) {
val text = btn.text
Toast.makeText(context, "" + text, Toast.LENGTH_LONG).show()
return
}
}
}
})
rg.addView(rdbtn)
}
binding.root.interactionRadioGroup.addView(rg)
}
}
}
22 changes: 21 additions & 1 deletion app/src/main/res/layout/state_fragment.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?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">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View"/>
<variable
Expand All @@ -15,6 +16,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/linearLayout"
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
Expand All @@ -29,6 +31,24 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:id="@+id/interactionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:background="@color/white"
android:orientation="vertical"
android:padding="8dp"
app:layout_constraintBottom_toTopOf="@+id/dummy_audio_button"
app:layout_constraintTop_toBottomOf="@+id/linearLayout"
tools:layout_editor_absoluteX="8dp">
<RadioGroup
android:id="@+id/interactionRadioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
</RadioGroup>
</LinearLayout>
<Button
android:id="@+id/dummy_audio_button"
android:layout_width="wrap_content"
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@
<color name="white">#FFFFFF</color>
<!-- AUDIO COMPONENT -->
<color name="audioComponentBackground">@color/oppiaDarkBlue</color>

<color name="blue_100">#0F0086FB</color>
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
<color name="blue_200">#6B0086FB</color>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.oppia.app.player.state;

import android.provider.MediaStore
import android.view.View
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.Matchers.allOf
import org.junit.Test
import org.junit.runner.RunWith
import org.oppia.app.R
import org.oppia.app.home.HomeActivity
import org.oppia.app.player.exploration.ExplorationActivity
import android.widget.TextView
import android.widget.LinearLayout
import android.widget.RadioGroup
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.ViewAction
import androidx.test.platform.ui.UiController
import junit.framework.TestCase.assertEquals
import org.hamcrest.Matcher

/** Tests for [StateFragmentPresenter]. */
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
@RunWith(AndroidJUnit4::class)
class StateFragmentPresenterTest {
var interactionInstanceId: String? = null

@Test
fun testMultipleSelectionInputInteraction() {
veena14cs marked this conversation as resolved.
Show resolved Hide resolved
ActivityScenario.launch(ExplorationActivity::class.java).use {
interactionInstanceId = "MultipleChoiceInput"
assertEquals(interactionInstanceId, "MultipleChoiceInput")
onView(withId(R.id.interactionRadioGroup)).perform(object : ViewAction {
veena14cs marked this conversation as resolved.
Show resolved Hide resolved

override fun getConstraints(): Matcher<View> {
return isDisplayed()
}

override fun getDescription(): String {
return "Performing click"
}

override fun perform(uiController: androidx.test.espresso.UiController?, view: View?) {
val parentRadioGroup = view as RadioGroup
val linearLayout = parentRadioGroup.getChildAt(0) as RadioGroup
val radioButton = linearLayout.getChildAt(0) as CustomRadioButton
radioButton.performClick()
}
})
}
}

@Test
fun testItemSelectionInputInteraction() {
ActivityScenario.launch(ExplorationActivity::class.java).use {
interactionInstanceId = "ItemSelectionInput"
assertEquals(interactionInstanceId, "ItemSelectionInput")
onView(withId(R.id.interactionContainer)).perform(object : ViewAction {

override fun getConstraints(): Matcher<View> {
return isDisplayed()
}

override fun getDescription(): String {
return "Performing click"
}

override fun perform(uiController: androidx.test.espresso.UiController?, view: View?) {
val parentLinearLayout = view as LinearLayout
val checkbox = parentLinearLayout.getChildAt(1) as CustomCheckbox
checkbox.performClick()
}
})
}
}
}
3 changes: 2 additions & 1 deletion utility/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ dependencies {
'androidx.appcompat:appcompat:1.0.2',
'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03',
'com.google.dagger:dagger:2.24',
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version",
'com.github.bumptech.glide:glide:4.9.0',
)
testImplementation(
'androidx.test.ext:junit:1.1.1',
Expand Down
Loading