Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ import org.hl7.fhir.r4.model.QuestionnaireResponse
internal sealed interface QuestionnaireAdapterItem {
/** A row for a question in a Questionnaire RecyclerView. */
data class Question(val item: QuestionnaireViewItem) :
QuestionnaireAdapterItem, ReviewAdapterItem
QuestionnaireAdapterItem, ReviewAdapterItem {
var id: String? = item.questionnaireItem.linkId
}

/** A row for a repeated group response instance's header. */
data class RepeatedGroupHeader(
val id: String,
/** The response index. This is 0-indexed, but should be 1-indexed when rendered in the UI. */
val index: Int,
/** Callback that is invoked when the user clicks the delete button. */
Expand All @@ -37,6 +40,7 @@ internal sealed interface QuestionnaireAdapterItem {
) : QuestionnaireAdapterItem

data class RepeatedGroupAddButton(
var id: String?,
val item: QuestionnaireViewItem,
) : QuestionnaireAdapterItem

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,17 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.VisibleForTesting
import androidx.appcompat.view.ContextThemeWrapper
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.res.use
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
Expand All @@ -33,9 +41,11 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.fhir.datacapture.extensions.inflate
import com.google.android.fhir.datacapture.validation.Invalid
import com.google.android.fhir.datacapture.views.NavigationViewHolder
import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderFactory
import com.google.android.fhir.datacapture.views.factories.ReviewViewHolderFactory
import com.google.android.material.progressindicator.LinearProgressIndicator
import kotlinx.coroutines.launch
import org.hl7.fhir.r4.model.Questionnaire
Expand Down Expand Up @@ -93,8 +103,8 @@ class QuestionnaireFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val questionnaireEditRecyclerView =
view.findViewById<RecyclerView>(R.id.questionnaire_edit_recycler_view)
val questionnaireReviewRecyclerView =
view.findViewById<RecyclerView>(R.id.questionnaire_review_recycler_view)
val questionnaireReviewComposeView =
view.findViewById<ComposeView>(R.id.questionnaire_review_recycler_view)
val questionnaireTitle = view.findViewById<TextView>(R.id.questionnaire_title)

// This container frame floats at the bottom of the view to make navigation controls visible at
Expand Down Expand Up @@ -139,7 +149,6 @@ class QuestionnaireFragment : Fragment() {
view.findViewById(R.id.questionnaire_progress_indicator)
val questionnaireEditAdapter =
QuestionnaireEditAdapter(questionnaireItemViewHolderFactoryMatchersProvider.get())
val questionnaireReviewAdapter = QuestionnaireReviewAdapter()

val reviewModeEditButton =
view.findViewById<View>(R.id.review_mode_edit_button).apply {
Expand All @@ -152,20 +161,16 @@ class QuestionnaireFragment : Fragment() {
// Animation does work well with views that could gain focus
questionnaireEditRecyclerView.itemAnimator = null

questionnaireReviewRecyclerView.adapter = questionnaireReviewAdapter
questionnaireReviewRecyclerView.layoutManager = LinearLayoutManager(view.context)

// Listen to updates from the view model.
viewLifecycleOwner.lifecycleScope.launchWhenCreated {
viewModel.questionnaireStateFlow.collect { state ->
when (val displayMode = state.displayMode) {
is DisplayMode.ReviewMode -> {
// Set items
questionnaireEditRecyclerView.visibility = View.GONE
questionnaireReviewAdapter.submitList(
state.items.filterIsInstance<ReviewAdapterItem>(),
)
questionnaireReviewRecyclerView.visibility = View.VISIBLE

questionnaireReviewComposeView.visibility = View.VISIBLE
questionnaireReviewComposeView.setContent { QuestionnaireReviewList(state.items) }
reviewModeEditButton.visibility =
if (displayMode.showEditButton) {
View.VISIBLE
Expand All @@ -189,7 +194,7 @@ class QuestionnaireFragment : Fragment() {
}
is DisplayMode.EditMode -> {
// Set items
questionnaireReviewRecyclerView.visibility = View.GONE
questionnaireReviewComposeView.visibility = View.GONE
questionnaireEditAdapter.submitList(state.items)
questionnaireEditRecyclerView.visibility = View.VISIBLE
reviewModeEditButton.visibility = View.GONE
Expand Down Expand Up @@ -234,7 +239,7 @@ class QuestionnaireFragment : Fragment() {
}
}
is DisplayMode.InitMode -> {
questionnaireReviewRecyclerView.visibility = View.GONE
questionnaireReviewComposeView.visibility = View.GONE
questionnaireEditRecyclerView.visibility = View.GONE
questionnaireProgressIndicator.visibility = View.GONE
reviewModeEditButton.visibility = View.GONE
Expand Down Expand Up @@ -283,6 +288,53 @@ class QuestionnaireFragment : Fragment() {
}
}

@Composable
private fun QuestionnaireReviewList(items: List<QuestionnaireAdapterItem>) {
LazyColumn {
items(
items = items,
key = { item ->
when (item) {
is QuestionnaireAdapterItem.Question -> item.id
?: throw IllegalStateException("Missing id for the Question: $item")
is QuestionnaireAdapterItem.RepeatedGroupHeader -> item.id
is QuestionnaireAdapterItem.Navigation -> "navigation"
is QuestionnaireAdapterItem.RepeatedGroupAddButton -> item.id
?: throw IllegalStateException("Missing id for the RepeatedGroupAddButton: $item")
}
},
) { item: QuestionnaireAdapterItem ->
AndroidView(
factory = { context ->
LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
when (item) {
is QuestionnaireAdapterItem.Question -> {
val viewHolder = ReviewViewHolderFactory.create(this)
viewHolder.bind(item.item)
addView(viewHolder.itemView)
}
is QuestionnaireAdapterItem.Navigation -> {
val viewHolder =
NavigationViewHolder(inflate(R.layout.pagination_navigation_view))
viewHolder.bind(item.questionnaireNavigationUIState)
addView(viewHolder.itemView)
}
is QuestionnaireAdapterItem.RepeatedGroupHeader -> {
TODO("Not implemented yet")
}
is QuestionnaireAdapterItem.RepeatedGroupAddButton -> {
TODO("Not implemented yet")
}
}
}
},
modifier = Modifier.fillMaxWidth(),
)
}
}
}

/** Calculates the progress percentage from given [count] and [totalCount] values. */
internal fun calculateProgressPercentage(count: Int, totalCount: Int): Int {
return if (totalCount == 0) 0 else (count * 100 / totalCount)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,7 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
// Case 3
add(
QuestionnaireAdapterItem.RepeatedGroupHeader(
id = "${index}_${question.item.questionnaireItem.linkId}",
index = index,
onDeleteClicked = { viewModelScope.launch { question.item.removeAnswerAt(index) } },
responses = nestedResponseItemList,
Expand All @@ -1017,16 +1018,31 @@ internal class QuestionnaireViewModel(application: Application, state: SavedStat
}
addAll(
getQuestionnaireAdapterItems(
// If nested display item is identified as instructions or flyover, then do not create
// questionnaire state for it.
questionnaireItemList = questionnaireItem.item.filterNot { it.isDisplayItem },
questionnaireResponseItemList = nestedResponseItemList,
),
// If nested display item is identified as instructions or flyover, then do not
// create
// questionnaire state for it.
questionnaireItemList = questionnaireItem.item.filterNot { it.isDisplayItem },
questionnaireResponseItemList = nestedResponseItemList,
)
.onEach {
// Reset the question id to avoid duplicate keys in LazyColumn composable. The new
// id is derived from the the repeated group index, the parent question
// questionnaire item linkId and the linkId of the nested questions
if (it is QuestionnaireAdapterItem.Question) {
it.id =
"${index}_${question.item.questionnaireItem.linkId}_${it.item.questionnaireItem.linkId}"
}
},
)
}

if (questionnaireItem.isRepeatedGroup) {
add(QuestionnaireAdapterItem.RepeatedGroupAddButton(question.item))
add(
QuestionnaireAdapterItem.RepeatedGroupAddButton(
id = "${question.item.questionnaireItem.linkId}_add_btn",
item = question.item,
),
)
}
}
currentPageItems = items
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
app:layout_constraintTop_toBottomOf="@+id/questionnaire_progress_indicator"
/>

<androidx.recyclerview.widget.RecyclerView
<androidx.compose.ui.platform.ComposeView
android:id="@+id/questionnaire_review_recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
Expand Down

This file was deleted.

Loading