Repository: google/android-fhir
Issue: Fix bug with Radio Group widget which selects all radio buttons for a choice question 

To reproduce this error :
1. Select the HIV-Risk Assessment questionnaire 
2. Select the any answer from the first question
3. Scroll down such that first question is completely out of view
4. Scroll back up, you will see all options selected

Changed files:
datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest.kt
datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactory.kt

diff --git a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest.kt b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest.kt
index 6f42256ed..253673dd9 100644
--- a/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest.kt
+++ b/datacapture/src/androidTest/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest.kt
@@ -37,7 +37,7 @@ class QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest {
     private val viewHolder = QuestionnaireItemRadioGroupViewHolderFactory.create(parent)
 
     @Test
-    fun shouldSetHeaderText() {
+    fun bind_shouldSetHeaderText() {
         viewHolder.bind(
             QuestionnaireItemViewItem(
                 Questionnaire.Item.newBuilder().apply {
@@ -53,7 +53,7 @@ class QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest {
     }
 
     @Test
-    fun shouldCreateRadioButtons() {
+    fun bind_shouldCreateRadioButtons() {
         viewHolder.bind(
             QuestionnaireItemViewItem(
                 Questionnaire.Item.newBuilder().apply {
@@ -91,7 +91,7 @@ class QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest {
     }
 
     @Test
-    fun shouldSetRadioButtonsUnchecked() {
+    fun bind_noAnswer_shouldLeaveRadioButtonsUnchecked() {
         viewHolder.bind(
             QuestionnaireItemViewItem(
                 Questionnaire.Item.newBuilder().apply {
@@ -119,7 +119,7 @@ class QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest {
 
     @Test
     @UiThreadTest
-    fun shouldSetRadioButtonsChecked() {
+    fun bind_answer_shouldCheckRadioButton() {
         viewHolder.bind(
             QuestionnaireItemViewItem(
                 Questionnaire.Item.newBuilder().apply {
@@ -132,6 +132,15 @@ class QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest {
                                         .build()
                                 }.build()
                             }.build()
+                        }.build(),
+                        Questionnaire.Item.AnswerOption.newBuilder().apply {
+                            value = Questionnaire.Item.AnswerOption.ValueX.newBuilder().apply {
+                                coding = Coding.newBuilder().apply {
+                                    display = com.google.fhir.r4.core.String.newBuilder()
+                                        .setValue("Coding 2")
+                                        .build()
+                                }.build()
+                            }.build()
                         }.build()
                     ))
                 }.build(),
@@ -149,15 +158,19 @@ class QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest {
             )
         )
 
-        val radioButton =
-            viewHolder.itemView.findViewById<RadioGroup>(R.id.radio_group)
-                .getChildAt(0) as RadioButton
-        assertThat(radioButton.isChecked).isTrue()
+        assertThat(
+            (viewHolder.itemView.findViewById<RadioGroup>(R.id.radio_group)
+                .getChildAt(0) as RadioButton).isChecked
+        ).isTrue()
+        assertThat(
+            (viewHolder.itemView.findViewById<RadioGroup>(R.id.radio_group)
+                .getChildAt(1) as RadioButton).isChecked
+        ).isFalse()
     }
 
     @Test
     @UiThreadTest
-    fun shouldSetQuestionnaireResponseItemComponentAnswer() {
+    fun click_shouldSetQuestionnaireResponseItemAnswer() {
         val questionnaireItemViewItem = QuestionnaireItemViewItem(
             Questionnaire.Item.newBuilder().apply {
                 addAllAnswerOption(mutableListOf(
@@ -175,10 +188,53 @@ class QuestionnaireItemRadioGroupViewHolderFactoryInstrumentedTest {
             QuestionnaireResponse.Item.newBuilder()
         )
         viewHolder.bind(questionnaireItemViewItem)
-        viewHolder.itemView.findViewById<RadioGroup>(R.id.radio_group).getChildAt(0).performClick()
+        viewHolder.itemView.findViewById<RadioGroup>(R.id.radio_group).getChildAt(0)
+            .performClick()
 
         val answer = questionnaireItemViewItem.questionnaireResponseItemBuilder.answerBuilderList
         assertThat(answer.size).isEqualTo(1)
         assertThat(answer[0].value.coding.display.value).isEqualTo("Coding 1")
     }
+
+    @Test
+    @UiThreadTest
+    fun click_shouldCheckRadioButton() {
+        val questionnaireItemViewItem = QuestionnaireItemViewItem(
+            Questionnaire.Item.newBuilder().apply {
+                addAllAnswerOption(mutableListOf(
+                    Questionnaire.Item.AnswerOption.newBuilder().apply {
+                        value = Questionnaire.Item.AnswerOption.ValueX.newBuilder().apply {
+                            coding = Coding.newBuilder().apply {
+                                display = com.google.fhir.r4.core.String.newBuilder()
+                                    .setValue("Coding 1")
+                                    .build()
+                            }.build()
+                        }.build()
+                    }.build(),
+                    Questionnaire.Item.AnswerOption.newBuilder().apply {
+                        value = Questionnaire.Item.AnswerOption.ValueX.newBuilder().apply {
+                            coding = Coding.newBuilder().apply {
+                                display = com.google.fhir.r4.core.String.newBuilder()
+                                    .setValue("Coding 2")
+                                    .build()
+                            }.build()
+                        }.build()
+                    }.build()
+                ))
+            }.build(),
+            QuestionnaireResponse.Item.newBuilder()
+        )
+        viewHolder.bind(questionnaireItemViewItem)
+        viewHolder.itemView.findViewById<RadioGroup>(R.id.radio_group).getChildAt(0)
+            .performClick()
+
+        assertThat(
+            (viewHolder.itemView.findViewById<RadioGroup>(R.id.radio_group)
+                .getChildAt(0) as RadioButton).isChecked
+        ).isTrue()
+        assertThat(
+            (viewHolder.itemView.findViewById<RadioGroup>(R.id.radio_group)
+                .getChildAt(1) as RadioButton).isChecked
+        ).isFalse()
+    }
 }
diff --git a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactory.kt b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactory.kt
index 33cc7e51d..34f4fa464 100644
--- a/datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactory.kt
+++ b/datacapture/src/main/java/com/google/android/fhir/datacapture/views/QuestionnaireItemRadioGroupViewHolderFactory.kt
@@ -49,33 +49,30 @@ object QuestionnaireItemRadioGroupViewHolderFactory : QuestionnaireItemViewHolde
                 radioHeader.text = questionnaireItem.text.value
                 radioGroup.removeAllViews()
 
-                // TODO: support other answer types besides coding
+                var index = 0
                 questionnaireItem.answerOptionList.forEach {
                     radioGroup.addView(RadioButton(radioGroup.context).apply {
+                        id = index++ // Use the answer option index as radio button ID
                         text = it.value.coding.displayValue
                         layoutParams = ViewGroup.LayoutParams(
                             ViewGroup.LayoutParams.MATCH_PARENT,
                             ViewGroup.LayoutParams.WRAP_CONTENT
                         )
-                        if (questionnaireItemViewItem.singleAnswerOrNull
-                                ?.value?.coding?.equals(answer) == true) {
-                            this.isChecked = true
-                        }
-                        setOnCheckedChangeListener { buttonView, isChecked ->
-                            if (isChecked) {
-                                questionnaireResponseItemBuilder.clearAnswer().addAnswer(
-                                    QuestionnaireResponse.Item.Answer.newBuilder().apply {
-                                        value =
-                                            QuestionnaireResponse.Item.Answer.ValueX.newBuilder()
-                                                .apply {
-                                                    coding = it.value.coding
-                                                }.build()
-                                    }
-                                )
-                            }
-                        }
+                        this.isChecked = it.value.coding == answer
                     })
                 }
+                radioGroup.setOnCheckedChangeListener { _, checkedId ->
+                    questionnaireResponseItemBuilder.clearAnswer().addAnswer(
+                        QuestionnaireResponse.Item.Answer.newBuilder().apply {
+                            value =
+                                QuestionnaireResponse.Item.Answer.ValueX.newBuilder()
+                                    .apply {
+                                        coding = questionnaireItem
+                                            .answerOptionList[checkedId].value.coding
+                                    }.build()
+                        }
+                    )
+                }
             }
         }
 }