Skip to content

Commit

Permalink
Change of access to some components of SDC to make them available for… (
Browse files Browse the repository at this point in the history
#2070)

* Change of access to some components of SDC to make them available for custom components

* Change of access to some components of SDC to make them available for custom components

* Added documentation for each public functions to describe usage

* Update datacapture/src/main/java/com/google/android/fhir/datacapture/views/GroupHeaderView.kt

* Update datacapture/src/main/java/com/google/android/fhir/datacapture/extensions/MoreHeaderViews.kt

* Run spotless apply

---------

Co-authored-by: Jing Tang <[email protected]>
  • Loading branch information
khyativyasargus and jingtang10 authored Jul 12, 2023
1 parent cfd88dc commit 9cc2932
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ package com.google.android.fhir.datacapture
* https://www.hl7.org/fhir/valueset-item-type.html and
* http://hl7.org/fhir/R4/valueset-questionnaire-item-control.html.
*/
internal enum class QuestionnaireViewHolderType(val value: Int) {
enum class QuestionnaireViewHolderType(val value: Int) {
GROUP(0),
BOOLEAN_TYPE_PICKER(1),
DATE_PICKER(2),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import com.google.android.fhir.datacapture.views.QuestionnaireViewItem
import com.google.android.material.card.MaterialCardView
import org.hl7.fhir.r4.model.Questionnaire

internal fun TextView.updateTextAndVisibility(localizedText: Spanned? = null) {
/** Displays `localizedText` if it is not null or empty, or hides the [TextView]. */
fun TextView.updateTextAndVisibility(localizedText: Spanned? = null) {
text = localizedText
visibility =
if (localizedText.isNullOrEmpty()) {
Expand All @@ -39,7 +40,7 @@ internal fun TextView.updateTextAndVisibility(localizedText: Spanned? = null) {
}

/** Returns [VISIBLE] if any of the [view] is visible, [GONE] otherwise. */
internal fun getHeaderViewVisibility(vararg view: TextView): Int {
fun getHeaderViewVisibility(vararg view: TextView): Int {
if (view.any { it.visibility == VISIBLE }) {
return VISIBLE
}
Expand All @@ -51,7 +52,7 @@ internal fun getHeaderViewVisibility(vararg view: TextView): Int {
* visibility and click listener for the [helpButton] to allow users to access the help information
* and toggles the visibility for view [helpCardView].
*/
internal fun initHelpViews(
fun initHelpViews(
helpButton: Button,
helpCardView: MaterialCardView,
helpTextView: TextView,
Expand All @@ -77,7 +78,7 @@ internal fun initHelpViews(
* Appends ' *' to [Questionnaire.QuestionnaireItemComponent.localizedTextSpanned] text if
* [Questionnaire.QuestionnaireItemComponent.required] is true.
*/
internal fun appendAsteriskToQuestionText(
fun appendAsteriskToQuestionText(
context: Context,
questionnaireViewItem: QuestionnaireViewItem
): Spanned {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ import org.hl7.fhir.r4.model.Questionnaire
* [Questionnaire.QuestionnaireItemComponent.required] is true, or [R.string.optional_text] if
* [QuestionnaireViewItem.showOptionalText] is true.
*/
internal fun getRequiredOrOptionalText(
questionnaireViewItem: QuestionnaireViewItem,
context: Context
) =
fun getRequiredOrOptionalText(questionnaireViewItem: QuestionnaireViewItem, context: Context) =
when {
(questionnaireViewItem.questionnaireItem.required &&
questionnaireViewItem.questionViewTextConfiguration.showRequiredText) -> {
Expand All @@ -53,7 +50,7 @@ internal fun getRequiredOrOptionalText(
* true, the error message starts with `Required` text and the rest of the error message is placed
* on the next line.
*/
internal fun getValidationErrorMessage(
fun getValidationErrorMessage(
context: Context,
questionnaireViewItem: QuestionnaireViewItem,
validationResult: ValidationResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ import org.hl7.fhir.r4.model.StringType
import org.hl7.fhir.r4.utils.ToolingExtensions
import timber.log.Timber

/** UI controls relevant to capturing question data. */
internal enum class ItemControlTypes(
/**
* Item control types supported by the SDC library with `extensionCode` from the value set
* http://hl7.org/fhir/R4/valueset-questionnaire-item-control.html and `viewHolderType` as the
* [QuestionnaireViewHolderType] to be used to render the question.
*/
enum class ItemControlTypes(
val extensionCode: String,
val viewHolderType: QuestionnaireViewHolderType,
) {
Expand Down Expand Up @@ -153,8 +157,13 @@ internal fun Questionnaire.QuestionnaireItemComponent.isReferencedBy(
.contains(Regex(".*linkId='${this.linkId}'.*"))
}

// Item control code, or null
internal val Questionnaire.QuestionnaireItemComponent.itemControl: ItemControlTypes?
/**
* The [ItemControlTypes] of the questionnaire item if it is specified by the item control
* extension, or `null`.
*
* See http://hl7.org/fhir/R4/extension-questionnaire-itemcontrol.html.
*/
val Questionnaire.QuestionnaireItemComponent.itemControl: ItemControlTypes?
get() {
val codeableConcept =
this.extension
Expand All @@ -173,7 +182,12 @@ internal val Questionnaire.QuestionnaireItemComponent.itemControl: ItemControlTy
return ItemControlTypes.values().firstOrNull { it.extensionCode == code }
}

internal enum class ChoiceOrientationTypes(val extensionCode: String) {
/**
* The desired orientation for the list of choices.
*
* See http://hl7.org/fhir/R4/extension-questionnaire-choiceorientation.html.
*/
enum class ChoiceOrientationTypes(val extensionCode: String) {
HORIZONTAL("horizontal"),
VERTICAL("vertical")
}
Expand All @@ -182,7 +196,7 @@ internal const val EXTENSION_CHOICE_ORIENTATION_URL =
"http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation"

/** Desired orientation to render a list of choices. */
internal val Questionnaire.QuestionnaireItemComponent.choiceOrientation: ChoiceOrientationTypes?
val Questionnaire.QuestionnaireItemComponent.choiceOrientation: ChoiceOrientationTypes?
get() {
val code =
(this.extension.firstOrNull { it.url == EXTENSION_CHOICE_ORIENTATION_URL }?.value
Expand All @@ -194,7 +208,7 @@ internal val Questionnaire.QuestionnaireItemComponent.choiceOrientation: ChoiceO
internal const val EXTENSION_MIME_TYPE = "http://hl7.org/fhir/StructureDefinition/mimeType"

/** Identifies the kinds of attachment allowed to be sent for an element. */
internal val Questionnaire.QuestionnaireItemComponent.mimeTypes: List<String>
val Questionnaire.QuestionnaireItemComponent.mimeTypes: List<String>
get() {
return extension
.filter { it.url == EXTENSION_MIME_TYPE }
Expand All @@ -203,7 +217,7 @@ internal val Questionnaire.QuestionnaireItemComponent.mimeTypes: List<String>
}

/** Currently supported mime types. */
internal enum class MimeType(val value: String) {
enum class MimeType(val value: String) {
AUDIO("audio"),
DOCUMENT("application"),
IMAGE("image"),
Expand All @@ -214,12 +228,12 @@ internal enum class MimeType(val value: String) {
private fun getMimeType(mimeType: String): String = mimeType.substringBefore("/")

/** Returns true if at least one mime type matches the given type. */
internal fun Questionnaire.QuestionnaireItemComponent.hasMimeType(type: String): Boolean {
fun Questionnaire.QuestionnaireItemComponent.hasMimeType(type: String): Boolean {
return mimeTypes.any { it.substringBefore("/") == type }
}

/** Returns true if all mime types match the given type. */
internal fun Questionnaire.QuestionnaireItemComponent.hasMimeTypeOnly(type: String): Boolean {
fun Questionnaire.QuestionnaireItemComponent.hasMimeTypeOnly(type: String): Boolean {
return mimeTypes.all { it.substringBefore("/") == type }
}

Expand Down Expand Up @@ -271,7 +285,7 @@ internal val Questionnaire.QuestionnaireItemComponent.displayItemControl: Displa
}

/** Whether any one of the nested display item has [DisplayItemControlType.HELP] control. */
internal val Questionnaire.QuestionnaireItemComponent.hasHelpButton: Boolean
val Questionnaire.QuestionnaireItemComponent.hasHelpButton: Boolean
get() {
return item.any { it.isHelpCode }
}
Expand Down Expand Up @@ -299,11 +313,11 @@ val Questionnaire.QuestionnaireItemComponent.localizedPrefixSpanned: Spanned?
* A nested questionnaire item of type display with displayCategory extension with [INSTRUCTIONS]
* code is used as the instructions of the parent question.
*/
internal val Questionnaire.QuestionnaireItemComponent.localizedInstructionsSpanned: Spanned?
val Questionnaire.QuestionnaireItemComponent.localizedInstructionsSpanned: Spanned?
get() = item.localizedInstructionsSpanned

/** [localizedInstructionsSpanned] over list of [Questionnaire.QuestionnaireItemComponent] */
internal val List<Questionnaire.QuestionnaireItemComponent>.localizedInstructionsSpanned: Spanned?
val List<Questionnaire.QuestionnaireItemComponent>.localizedInstructionsSpanned: Spanned?
get() {
return this.firstOrNull { questionnaireItem ->
questionnaireItem.type == Questionnaire.QuestionnaireItemType.DISPLAY &&
Expand All @@ -320,7 +334,7 @@ internal val Questionnaire.QuestionnaireItemComponent.localizedFlyoverSpanned: S
get() = item.localizedFlyoverSpanned

/** [localizedFlyoverSpanned] over list of [Questionnaire.QuestionnaireItemComponent] */
internal val List<Questionnaire.QuestionnaireItemComponent>.localizedFlyoverSpanned: Spanned?
val List<Questionnaire.QuestionnaireItemComponent>.localizedFlyoverSpanned: Spanned?
get() =
this.firstOrNull { questionnaireItem ->
questionnaireItem.type == Questionnaire.QuestionnaireItemType.DISPLAY &&
Expand All @@ -332,11 +346,11 @@ internal val List<Questionnaire.QuestionnaireItemComponent>.localizedFlyoverSpan
* A nested questionnaire item of type display with displayCategory extension with [INSTRUCTIONS]
* code is used as the instructions of the parent question.
*/
internal val Questionnaire.QuestionnaireItemComponent.localizedHelpSpanned: Spanned?
val Questionnaire.QuestionnaireItemComponent.localizedHelpSpanned: Spanned?
get() = item.localizedHelpSpanned

/** [localizedHelpSpanned] over list of [Questionnaire.QuestionnaireItemComponent] */
internal val List<Questionnaire.QuestionnaireItemComponent>.localizedHelpSpanned: Spanned?
val List<Questionnaire.QuestionnaireItemComponent>.localizedHelpSpanned: Spanned?
get() {
return this.firstOrNull { questionnaireItem -> questionnaireItem.isHelpCode }
?.localizedTextSpanned
Expand Down Expand Up @@ -425,7 +439,7 @@ internal val Questionnaire.QuestionnaireItemComponent.isDisplayItem: Boolean
(isInstructionsCode || isFlyoverCode || isHelpCode))

/** Slider step extension value. */
internal val Questionnaire.QuestionnaireItemComponent.sliderStepValue: Int?
val Questionnaire.QuestionnaireItemComponent.sliderStepValue: Int?
get() {
val extension =
this.extension.singleOrNull { it.url == EXTENSION_SLIDER_STEP_VALUE_URL } ?: return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import android.widget.TextView
import com.google.android.fhir.datacapture.QuestionnaireViewHolderType
import com.google.android.fhir.datacapture.R
import com.google.android.fhir.datacapture.extensions.getHeaderViewVisibility
import com.google.android.fhir.datacapture.extensions.initHelpViews
import com.google.android.fhir.datacapture.extensions.localizedInstructionsSpanned
import com.google.android.fhir.datacapture.extensions.localizedPrefixSpanned
import com.google.android.fhir.datacapture.extensions.updateTextAndVisibility

internal class GroupHeaderView(context: Context, attrs: AttributeSet?) :
LinearLayout(context, attrs) {
/**
* Generic view for the prefix, question, and hint as the header of a group using a view holder of
* type [QuestionnaireViewHolderType.GROUP].
*/
class GroupHeaderView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {

init {
LayoutInflater.from(context).inflate(R.layout.group_type_header_view, this, true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import com.google.android.fhir.datacapture.extensions.updateTextAndVisibility
import org.hl7.fhir.r4.model.Questionnaire

/** View for the prefix, question, and hint of a questionnaire item. */
internal class HeaderView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
class HeaderView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {

init {
LayoutInflater.from(context).inflate(R.layout.header_view, this, true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,9 @@ data class QuestionnaireViewItem(
{
emptyList()
},
internal val draftAnswer: Any? = null,
internal val enabledDisplayItems: List<Questionnaire.QuestionnaireItemComponent> = emptyList(),
internal val questionViewTextConfiguration: QuestionTextConfiguration =
QuestionTextConfiguration(),
val draftAnswer: Any? = null,
val enabledDisplayItems: List<Questionnaire.QuestionnaireItemComponent> = emptyList(),
val questionViewTextConfiguration: QuestionTextConfiguration = QuestionTextConfiguration(),
) {

/**
Expand Down Expand Up @@ -129,7 +128,7 @@ data class QuestionnaireViewItem(
}

/** Adds an answer to the existing answers and removes the draft answer. */
internal fun addAnswer(
fun addAnswer(
questionnaireResponseItemAnswerComponent:
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent
) {
Expand All @@ -145,7 +144,7 @@ data class QuestionnaireViewItem(
}

/** Removes an answer from the existing answers, as well as any draft answer. */
internal fun removeAnswer(
fun removeAnswer(
questionnaireResponseItemAnswerComponent:
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent
) {
Expand All @@ -170,7 +169,11 @@ data class QuestionnaireViewItem(
answersChangedCallback(questionnaireItem, questionnaireResponseItem, listOf(), draftAnswer)
}

internal fun answerString(context: Context): String {
/**
* Returns a given answer (The respondent's answer(s) to the question) along with [displayString]
* if question is answered else 'Not Answered'
*/
fun answerString(context: Context): String {
if (!questionnaireResponseItem.hasAnswer()) return context.getString(R.string.not_answered)
return questionnaireResponseItem.answer.joinToString { it.value.displayString(context) }
}
Expand All @@ -195,7 +198,7 @@ data class QuestionnaireViewItem(
* options are defined in `Questionnaire.item.answerValueSet`, the answer value set will be
* expanded.
*/
internal val answerOption: List<Questionnaire.QuestionnaireItemAnswerOptionComponent>
val answerOption: List<Questionnaire.QuestionnaireItemAnswerOptionComponent>
get() =
runBlocking(Dispatchers.IO) {
when {
Expand All @@ -212,7 +215,7 @@ data class QuestionnaireViewItem(
* [Questionnaire.QuestionnaireResponseItemComponent] (derived from cqf-expression), otherwise it
* is derived from [localizedTextSpanned] of [QuestionnaireResponse.QuestionnaireItemComponent]
*/
internal val questionText: Spanned? by lazy {
val questionText: Spanned? by lazy {
questionnaireResponseItem.text?.toSpanned() ?: questionnaireItem.localizedTextSpanned
}

Expand Down

0 comments on commit 9cc2932

Please sign in to comment.