Skip to content

Commit

Permalink
itemAnswerMedia extension support added for image (#1749)
Browse files Browse the repository at this point in the history
* itemAnswerMedia extension support added for image

* added support for RTL

* test case updated for RTL support

* review comments addressed

* review comment addressed

* set Selected answer to QuestionnaireResponse updated

* filter test cases fixed for QuestionnaireItemDropDownViewHolderFactory

* filter test cases fixed for QuestionnaireItemDropDownViewHolderFactory

* review comment updated
  • Loading branch information
PallaviGanorkar committed Jan 12, 2023
1 parent 9fe7c81 commit 2b07e5d
Show file tree
Hide file tree
Showing 17 changed files with 301 additions and 53 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@

package com.google.android.fhir.datacapture

import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import org.hl7.fhir.r4.model.Attachment
import org.hl7.fhir.r4.model.BooleanType
import org.hl7.fhir.r4.model.Questionnaire

internal const val EXTENSION_OPTION_EXCLUSIVE_URL =
"http:https://hl7.org/fhir/StructureDefinition/questionnaire-optionExclusive"
internal const val EXTENSION_ITEM_ANSWER_MEDIA =
"http:https://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemAnswerMedia"

/** Indicates that if this answerOption is selected, no other possible answers may be selected. */
internal val Questionnaire.QuestionnaireItemAnswerOptionComponent.optionExclusive: Boolean
Expand All @@ -33,3 +40,26 @@ internal val Questionnaire.QuestionnaireItemAnswerOptionComponent.optionExclusiv
}
return false
}

fun Questionnaire.QuestionnaireItemAnswerOptionComponent.itemAnswerOptionImage(
context: Context
): Drawable? {
return (extension.singleOrNull { it.url == EXTENSION_ITEM_ANSWER_MEDIA }?.value as Attachment?)
?.let {
if (!it.hasContentType() || !it.hasData()) {
return null
}
when (it.contentType) {
"image/jpeg",
"image/jpg",
"image/png" -> {
val bitmap = BitmapFactory.decodeByteArray(it.data, 0, it.data.size)
val imageSize = context.resources.getDimensionPixelOffset(R.dimen.choice_button_image)
val drawable: Drawable = BitmapDrawable(context.resources, bitmap)
drawable.setBounds(0, 0, imageSize, imageSize)
drawable
}
else -> null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.itemAnswerOptionImage
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.util.concurrent.atomic.AtomicInteger
Expand Down Expand Up @@ -197,6 +198,12 @@ private class OptionSelectAdapter(val multiSelectEnabled: Boolean) :
(holder as OptionSelectViewHolder.OptionSingle).radioButton
}
compoundButton.text = item.option.displayString
compoundButton.setCompoundDrawablesRelative(
item.option.item.itemAnswerOptionImage(compoundButton.context),
null,
null,
null
)
compoundButton.setOnCheckedChangeListener(null)
compoundButton.isChecked = item.option.selected
compoundButton.setOnCheckedChangeListener { _, checked ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ internal object QuestionnaireItemAutoCompleteViewHolderFactory :
val adapter =
ArrayAdapter(
header.context,
R.layout.questionnaire_item_drop_down_list,
R.layout.questionnaire_item_drop_down_list_item,
answerOptionString
)
autoCompleteTextView.setAdapter(adapter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.google.android.fhir.datacapture.ChoiceOrientationTypes
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.choiceOrientation
import com.google.android.fhir.datacapture.common.datatype.displayString
import com.google.android.fhir.datacapture.itemAnswerOptionImage
import com.google.android.fhir.datacapture.optionExclusive
import com.google.android.fhir.datacapture.validation.Invalid
import com.google.android.fhir.datacapture.validation.NotValidated
Expand Down Expand Up @@ -105,6 +106,12 @@ internal object QuestionnaireItemCheckBoxGroupViewHolderFactory :
checkboxLayout.findViewById<CheckBox>(R.id.check_box).apply {
id = viewId
text = answerOption.value.displayString(header.context)
setCompoundDrawablesRelative(
answerOption.itemAnswerOptionImage(checkboxGroup.context),
null,
null,
null
)
isChecked = questionnaireItemViewItem.isAnswerOptionSelected(answerOption)
layoutParams =
ViewGroup.LayoutParams(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
package com.google.android.fhir.datacapture.views

import android.content.Context
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.TextView
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.common.datatype.displayString
import com.google.android.fhir.datacapture.itemAnswerOptionImage
import com.google.android.fhir.datacapture.localizedFlyoverSpanned
import com.google.android.fhir.datacapture.validation.Invalid
import com.google.android.fhir.datacapture.validation.NotValidated
Expand All @@ -30,6 +35,7 @@ import com.google.android.fhir.datacapture.validation.ValidationResult
import com.google.android.material.textfield.MaterialAutoCompleteTextView
import com.google.android.material.textfield.TextInputLayout
import org.hl7.fhir.r4.model.QuestionnaireResponse
import timber.log.Timber

internal object QuestionnaireItemDropDownViewHolderFactory :
QuestionnaireItemViewHolderFactory(R.layout.questionnaire_item_drop_down_view) {
Expand All @@ -52,29 +58,51 @@ internal object QuestionnaireItemDropDownViewHolderFactory :
cleanupOldState()
header.bind(questionnaireItemViewItem.questionnaireItem)
textInputLayout.hint = questionnaireItemViewItem.questionnaireItem.localizedFlyoverSpanned
val answerOptionString =
val answerOptionList =
this.questionnaireItemViewItem.answerOption
.map { it.value.displayString(context) }
.map {
DropDownAnswerOption(
it.value.displayString(context),
it.itemAnswerOptionImage(context)
)
}
.toMutableList()
answerOptionString.add(0, context.getString(R.string.hyphen))
answerOptionList.add(0, DropDownAnswerOption(context.getString(R.string.hyphen), null))
val adapter =
ArrayAdapter(context, R.layout.questionnaire_item_drop_down_list, answerOptionString)
questionnaireItemViewItem.answers
AnswerOptionDropDownArrayAdapter(
context,
R.layout.questionnaire_item_drop_down_list_item,
answerOptionList
)
val selectedAnswer =
questionnaireItemViewItem.answers.singleOrNull()?.value?.displayString(header.context)
answerOptionList
.filter { it.answerOptionString == selectedAnswer }
.singleOrNull()
?.value
?.displayString(header.context)
?.let {
autoCompleteTextView.setText(it)
autoCompleteTextView.setSelection(it.length)
autoCompleteTextView.setText(it.answerOptionString)
autoCompleteTextView.setSelection(it.answerOptionString.length)
autoCompleteTextView.setCompoundDrawablesRelative(
it.answerOptionImage,
null,
null,
null
)
}
autoCompleteTextView.setAdapter(adapter)
autoCompleteTextView.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
val selectedItem = adapter.getItem(position)
autoCompleteTextView.setText(selectedItem?.answerOptionString, false)
autoCompleteTextView.setCompoundDrawablesRelative(
adapter.getItem(position)?.answerOptionImage,
null,
null,
null
)
val selectedAnswer =
questionnaireItemViewItem.answerOption
.firstOrNull {
it.value.displayString(context) == autoCompleteTextView.adapter.getItem(position)
}
.firstOrNull { it.value.displayString(context) == selectedItem?.answerOptionString }
?.value

if (selectedAnswer == null) {
Expand Down Expand Up @@ -104,6 +132,42 @@ internal object QuestionnaireItemDropDownViewHolderFactory :
private fun cleanupOldState() {
autoCompleteTextView.setAdapter(null)
autoCompleteTextView.text = null
autoCompleteTextView.setCompoundDrawablesRelative(null, null, null, null)
}
}
}

internal class AnswerOptionDropDownArrayAdapter(
context: Context,
private val layoutResourceId: Int,
answerOption: List<DropDownAnswerOption>
) : ArrayAdapter<DropDownAnswerOption>(context, layoutResourceId, answerOption) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val listItemView =
convertView ?: LayoutInflater.from(parent.context).inflate(layoutResourceId, parent, false)
try {
val answerOption: DropDownAnswerOption? = getItem(position)
val answerOptionTextView =
listItemView?.findViewById<View>(R.id.answer_option_textview) as TextView
answerOptionTextView.text = answerOption?.answerOptionString
answerOptionTextView.setCompoundDrawablesRelative(
answerOption?.answerOptionImage,
null,
null,
null
)
} catch (e: Exception) {
Timber.w("Could not set data to dropdown UI", e)
}
return listItemView
}
}

internal data class DropDownAnswerOption(
val answerOptionString: String,
val answerOptionImage: Drawable?
) {
override fun toString(): String {
return this.answerOptionString
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.google.android.fhir.datacapture.ChoiceOrientationTypes
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.choiceOrientation
import com.google.android.fhir.datacapture.common.datatype.displayString
import com.google.android.fhir.datacapture.itemAnswerOptionImage
import com.google.android.fhir.datacapture.validation.Invalid
import com.google.android.fhir.datacapture.validation.NotValidated
import com.google.android.fhir.datacapture.validation.Valid
Expand Down Expand Up @@ -104,6 +105,12 @@ internal object QuestionnaireItemRadioGroupViewHolderFactory :
radioButtonItem.findViewById<RadioButton>(R.id.radio_button).apply {
id = viewId
text = answerOption.value.displayString(header.context)
setCompoundDrawablesRelative(
answerOption.itemAnswerOptionImage(radioGroup.context),
null,
null,
null
)
layoutParams =
ViewGroup.LayoutParams(
when (choiceOrientation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/item_margin_horizontal"
android:drawablePadding="@dimen/item_margin_horizontal"
/>
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
-->
<TextView
xmlns:android="http:https://schemas.android.com/apk/res/android"
android:id="@+id/answer_option_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/item_margin_horizontal"
android:ellipsize="end"
android:maxLines="1"
android:padding="@dimen/padding_drop_down"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<com.google.android.material.textfield.MaterialAutoCompleteTextView
style="?attr/questionnaireDropDownSelectedTextStyle"
android:id="@+id/auto_complete"
android:drawablePadding="@dimen/item_margin_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
style="?attr/questionnaireCheckBoxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/item_margin_horizontal"
tools:text="Option"
/>
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
limitations under the License.
-->
<RadioButton
style="?attr/questionnaireRadioButtonStyle"
xmlns:android="http:https://schemas.android.com/apk/res/android"
android:id="@+id/radio_button"
style="?attr/questionnaireRadioButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/item_margin_horizontal"
/>
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<RadioButton
xmlns:android="http:https://schemas.android.com/apk/res/android"
style="?attr/questionnaireRadioButtonStyle"
android:id="@+id/radio_button"
style="?attr/questionnaireRadioButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/item_margin_horizontal"
android:drawablePadding="@dimen/item_margin_horizontal"
/>
1 change: 1 addition & 0 deletions datacapture/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<dimen name="help_header_margin_bottom">4dp</dimen>
<dimen name="help_margin_bottom">16dp</dimen>
<dimen name="icon_inset">16dp</dimen>
<dimen name="choice_button_image">48dp</dimen>
<!-- dimens for padding between text and icon in the radio button -->
<dimen name="padding_between_text_and_icon">16dp</dimen>
<dimen name="padding_after_text">24dp</dimen>
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,11 @@ class QuestionnaireItemDropDownViewHolderFactoryTest {
answersChangedCallback = { _, _, _ -> },
)
)
val selectedItem =
viewHolder.itemView.findViewById<AutoCompleteTextView>(R.id.auto_complete).adapter.getItem(1)
as DropDownAnswerOption

assertThat(
viewHolder.itemView
.findViewById<AutoCompleteTextView>(R.id.auto_complete)
.adapter
.getItem(1)
.toString()
)
.isEqualTo("Test Code")
assertThat(selectedItem.answerOptionString).isEqualTo("Test Code")
}

@Test
Expand All @@ -101,15 +97,11 @@ class QuestionnaireItemDropDownViewHolderFactoryTest {
answersChangedCallback = { _, _, _ -> },
)
)
val selectedItem =
viewHolder.itemView.findViewById<AutoCompleteTextView>(R.id.auto_complete).adapter.getItem(1)
as DropDownAnswerOption

assertThat(
viewHolder.itemView
.findViewById<AutoCompleteTextView>(R.id.auto_complete)
.adapter
.getItem(1)
.toString()
)
.isEqualTo("John Doe")
assertThat(selectedItem.answerOptionString).isEqualTo("John Doe")
}

@Test
Expand All @@ -126,15 +118,11 @@ class QuestionnaireItemDropDownViewHolderFactoryTest {
answersChangedCallback = { _, _, _ -> },
)
)
val selectedItem =
viewHolder.itemView.findViewById<AutoCompleteTextView>(R.id.auto_complete).adapter.getItem(1)
as DropDownAnswerOption

assertThat(
viewHolder.itemView
.findViewById<AutoCompleteTextView>(R.id.auto_complete)
.adapter
.getItem(1)
.toString()
)
.isEqualTo("Patient/123")
assertThat(selectedItem.answerOptionString).isEqualTo("Patient/123")
}

@Test
Expand All @@ -151,15 +139,10 @@ class QuestionnaireItemDropDownViewHolderFactoryTest {
answersChangedCallback = { _, _, _ -> },
)
)

assertThat(
viewHolder.itemView
.findViewById<AutoCompleteTextView>(R.id.auto_complete)
.adapter
.getItem(1)
.toString()
)
.isEqualTo("test-code")
val selectedItem =
viewHolder.itemView.findViewById<AutoCompleteTextView>(R.id.auto_complete).adapter.getItem(1)
as DropDownAnswerOption
assertThat(selectedItem.answerOptionString).isEqualTo("test-code")
}

@Test
Expand Down
Loading

0 comments on commit 2b07e5d

Please sign in to comment.