Skip to content

Commit

Permalink
[wip] configure passing bottom nav state params
Browse files Browse the repository at this point in the history
  • Loading branch information
LZRS committed Aug 17, 2023
1 parent 0a1f03f commit 1c3d155
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 143 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.google.android.fhir.datacapture

sealed class DisplayMode {
class EditMode(val pagination: QuestionnairePagination) : DisplayMode()
data class ReviewMode(val showEditButton: Boolean, val showSubmitButton: Boolean) : DisplayMode()

// Sentinel displayMode that's used in setting the initial default QuestionnaireState
object InitMode : DisplayMode()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.google.android.fhir.datacapture

import android.view.View
import android.widget.Button
import androidx.recyclerview.widget.RecyclerView
import com.google.android.fhir.datacapture.DisplayMode

class NavigationViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {

fun bind(displayMode: DisplayMode){
val paginationPreviousButton = itemView.findViewById<View>(R.id.pagination_previous_button)
val paginationNextButton = itemView.findViewById<View>(R.id.pagination_next_button)
val submitButton = itemView.findViewById<Button>(R.id.submit_questionnaire)
val reviewModeButton = itemView.findViewById<View>(R.id.review_mode_button)

when(displayMode){
is DisplayMode.ReviewMode -> {
// Set button visibility
submitButton.visibility = if (displayMode.showSubmitButton) View.VISIBLE else View.GONE
reviewModeButton.visibility = View.GONE
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE
}
is DisplayMode.EditMode -> {
// Set button visibility
submitButton.visibility =
if (displayMode.pagination.showSubmitButton) View.VISIBLE else View.GONE
reviewModeButton.visibility =
if (displayMode.pagination.showReviewButton) View.VISIBLE else View.GONE
if (displayMode.pagination.isPaginated) {
paginationPreviousButton.visibility = View.VISIBLE
paginationPreviousButton.isEnabled = displayMode.pagination.hasPreviousPage
paginationNextButton.visibility = View.VISIBLE
paginationNextButton.isEnabled = displayMode.pagination.hasNextPage
} else {
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE
}
}
is DisplayMode.InitMode -> {
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE
submitButton.visibility = View.GONE
reviewModeButton.visibility = View.GONE
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ import com.google.android.fhir.datacapture.views.QuestionnaireViewItem
internal sealed interface QuestionnaireAdapterItem {
/** A row for a question in a Questionnaire RecyclerView. */
data class Question(val item: QuestionnaireViewItem) : QuestionnaireAdapterItem

// A row for bottom navigation
data class NavElement(val displayMode: DisplayMode): QuestionnaireAdapterItem
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package com.google.android.fhir.datacapture

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.fhir.datacapture.contrib.views.PhoneNumberViewHolderFactory
import com.google.android.fhir.datacapture.extensions.itemControl
import com.google.android.fhir.datacapture.views.QuestionnaireViewItem
Expand Down Expand Up @@ -46,16 +49,20 @@ internal class QuestionnaireEditAdapter(
private val questionnaireItemViewHolderMatchers:
List<QuestionnaireFragment.QuestionnaireItemViewHolderFactoryMatcher> =
emptyList(),
) : ListAdapter<QuestionnaireAdapterItem, QuestionnaireItemViewHolder>(DiffCallbacks.ITEMS) {
private val onBottomNavCreated: (View) -> Unit = {}
) : ListAdapter<QuestionnaireAdapterItem, RecyclerView.ViewHolder>(DiffCallbacks.ITEMS) {
/**
* @param viewType the integer value of the [QuestionnaireViewHolderType] used to render the
* [QuestionnaireViewItem].
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuestionnaireItemViewHolder {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val typedViewType = ViewType.parse(viewType)
val subtype = typedViewType.subtype
return when (typedViewType.type) {
ViewType.Type.QUESTION -> onCreateViewHolderQuestion(parent = parent, subtype = subtype)
ViewType.Type.NAVIGATION -> NavigationViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.questionnaire_fragment_bottom_nav_view, parent, false)
.also { onBottomNavCreated.invoke(it) })
}
}

Expand Down Expand Up @@ -97,11 +104,16 @@ internal class QuestionnaireEditAdapter(
return viewHolderFactory.create(parent)
}

override fun onBindViewHolder(holder: QuestionnaireItemViewHolder, position: Int) {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val item = getItem(position)) {
is QuestionnaireAdapterItem.Question -> {
holder as QuestionnaireItemViewHolder
holder.bind(item.item)
}
is QuestionnaireAdapterItem.NavElement -> {
holder as NavigationViewHolder
holder.bind(item.displayMode)
}
}
}

Expand All @@ -118,6 +130,10 @@ internal class QuestionnaireEditAdapter(
type = ViewType.Type.QUESTION
subtype = getItemViewTypeForQuestion(item.item)
}
is QuestionnaireAdapterItem.NavElement -> {
type = ViewType.Type.NAVIGATION
subtype = 0 // todo: change
}
}
return ViewType.from(type = type, subtype = subtype).viewType
}
Expand Down Expand Up @@ -147,6 +163,7 @@ internal class QuestionnaireEditAdapter(

enum class Type {
QUESTION,
NAVIGATION
}
}

Expand Down Expand Up @@ -258,6 +275,9 @@ internal object DiffCallbacks {
newItem is QuestionnaireAdapterItem.Question &&
QUESTIONS.areItemsTheSame(oldItem, newItem)
}
is QuestionnaireAdapterItem.NavElement -> {
newItem is QuestionnaireAdapterItem.NavElement && oldItem.displayMode == newItem.displayMode
}
}

override fun areContentsTheSame(
Expand All @@ -269,6 +289,9 @@ internal object DiffCallbacks {
newItem is QuestionnaireAdapterItem.Question &&
QUESTIONS.areContentsTheSame(oldItem, newItem)
}
is QuestionnaireAdapterItem.NavElement -> {
newItem is QuestionnaireAdapterItem.NavElement && oldItem.displayMode == newItem.displayMode
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.fhir.datacapture.validation.Invalid
import com.google.android.fhir.datacapture.views.NavState
import com.google.android.fhir.datacapture.views.factories.QuestionnaireItemViewHolderFactory
import com.google.android.material.progressindicator.LinearProgressIndicator
import org.hl7.fhir.r4.model.Questionnaire
Expand Down Expand Up @@ -92,43 +93,18 @@ class QuestionnaireFragment : Fragment() {
view.findViewById<RecyclerView>(R.id.questionnaire_edit_recycler_view)
val questionnaireReviewRecyclerView =
view.findViewById<RecyclerView>(R.id.questionnaire_review_recycler_view)
val paginationPreviousButton = view.findViewById<View>(R.id.pagination_previous_button)
paginationPreviousButton.setOnClickListener { viewModel.goToPreviousPage() }
val paginationNextButton = view.findViewById<View>(R.id.pagination_next_button)
paginationNextButton.setOnClickListener { viewModel.goToNextPage() }
view.findViewById<Button>(R.id.submit_questionnaire).setOnClickListener {
viewModel.validateQuestionnaireAndUpdateUI().let { validationMap ->
if (validationMap.values.flatten().filterIsInstance<Invalid>().isEmpty()) {
setFragmentResult(SUBMIT_REQUEST_KEY, Bundle.EMPTY)
} else {
val errorViewModel: QuestionnaireValidationErrorViewModel by activityViewModels()
errorViewModel.setQuestionnaireAndValidation(viewModel.questionnaire, validationMap)
QuestionnaireValidationErrorMessageDialogFragment()
.show(
requireActivity().supportFragmentManager,
QuestionnaireValidationErrorMessageDialogFragment.TAG
)
}
}
}
setUpBottomNav(view)
val questionnaireProgressIndicator: LinearProgressIndicator =
view.findViewById(R.id.questionnaire_progress_indicator)
val questionnaireEditAdapter =
QuestionnaireEditAdapter(questionnaireItemViewHolderFactoryMatchersProvider.get())
QuestionnaireEditAdapter(questionnaireItemViewHolderFactoryMatchersProvider.get(), onBottomNavCreated = ::setUpBottomNav)
val questionnaireReviewAdapter = QuestionnaireReviewAdapter()

val submitButton = requireView().findViewById<Button>(R.id.submit_questionnaire)

val reviewModeEditButton =
view.findViewById<View>(R.id.review_mode_edit_button).apply {
setOnClickListener { viewModel.setReviewMode(false) }
}

val reviewModeButton =
view.findViewById<View>(R.id.review_mode_button).apply {
setOnClickListener { viewModel.setReviewMode(true) }
}

questionnaireEditRecyclerView.adapter = questionnaireEditAdapter
val linearLayoutManager = LinearLayoutManager(view.context)
questionnaireEditRecyclerView.layoutManager = linearLayoutManager
Expand All @@ -150,42 +126,23 @@ class QuestionnaireFragment : Fragment() {
)
questionnaireReviewRecyclerView.visibility = View.VISIBLE

// Set button visibility
submitButton.visibility = if (displayMode.showSubmitButton) View.VISIBLE else View.GONE
reviewModeButton.visibility = View.GONE
reviewModeEditButton.visibility =
if (displayMode.showEditButton) {
View.VISIBLE
} else {
View.GONE
}
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE

// Hide progress indicator
questionnaireProgressIndicator.visibility = View.GONE
}
is DisplayMode.EditMode -> {
// Set items
questionnaireReviewRecyclerView.visibility = View.GONE
questionnaireEditAdapter.submitList(state.items)
questionnaireEditAdapter.submitList(state.items + QuestionnaireAdapterItem.NavElement(displayMode))
questionnaireEditRecyclerView.visibility = View.VISIBLE

// Set button visibility
submitButton.visibility =
if (displayMode.pagination.showSubmitButton) View.VISIBLE else View.GONE
reviewModeButton.visibility =
if (displayMode.pagination.showReviewButton) View.VISIBLE else View.GONE
reviewModeEditButton.visibility = View.GONE
if (displayMode.pagination.isPaginated) {
paginationPreviousButton.visibility = View.VISIBLE
paginationPreviousButton.isEnabled = displayMode.pagination.hasPreviousPage
paginationNextButton.visibility = View.VISIBLE
paginationNextButton.isEnabled = displayMode.pagination.hasNextPage
} else {
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE
}

// Set progress indicator
questionnaireProgressIndicator.visibility = View.VISIBLE
Expand Down Expand Up @@ -219,14 +176,11 @@ class QuestionnaireFragment : Fragment() {
is DisplayMode.InitMode -> {
questionnaireReviewRecyclerView.visibility = View.GONE
questionnaireEditRecyclerView.visibility = View.GONE
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE
questionnaireProgressIndicator.visibility = View.GONE
submitButton.visibility = View.GONE
reviewModeButton.visibility = View.GONE
reviewModeEditButton.visibility = View.GONE
}
}
updateBottomNavViews(view, state.displayMode)
}
}
requireActivity().supportFragmentManager.setFragmentResultListener(
Expand All @@ -249,6 +203,69 @@ class QuestionnaireFragment : Fragment() {
}
}

private fun setUpBottomNav(view: View){
val paginationPreviousButton = view.findViewById<View>(R.id.pagination_previous_button)
paginationPreviousButton.setOnClickListener { viewModel.goToPreviousPage() }
val paginationNextButton = view.findViewById<View>(R.id.pagination_next_button)
paginationNextButton.setOnClickListener { viewModel.goToNextPage() }
view.findViewById<Button>(R.id.submit_questionnaire).setOnClickListener {
viewModel.validateQuestionnaireAndUpdateUI().let { validationMap ->
if (validationMap.values.flatten().filterIsInstance<Invalid>().isEmpty()) {
setFragmentResult(SUBMIT_REQUEST_KEY, Bundle.EMPTY)
} else {
val errorViewModel: QuestionnaireValidationErrorViewModel by activityViewModels()
errorViewModel.setQuestionnaireAndValidation(viewModel.questionnaire, validationMap)
QuestionnaireValidationErrorMessageDialogFragment()
.show(
requireActivity().supportFragmentManager,
QuestionnaireValidationErrorMessageDialogFragment.TAG
)
}
}
}

view.findViewById<View>(R.id.review_mode_button).setOnClickListener { viewModel.setReviewMode(true) }
}

private fun updateBottomNavViews(view: View, displayMode: DisplayMode){
val paginationPreviousButton = view.findViewById<View>(R.id.pagination_previous_button)
val paginationNextButton = view.findViewById<View>(R.id.pagination_next_button)
val submitButton = view.findViewById<Button>(R.id.submit_questionnaire)
val reviewModeButton = view.findViewById<View>(R.id.review_mode_button)

when(displayMode){
is DisplayMode.ReviewMode -> {
// Set button visibility
submitButton.visibility = if (displayMode.showSubmitButton) View.VISIBLE else View.GONE
reviewModeButton.visibility = View.GONE
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE
}
is DisplayMode.EditMode -> {
// Set button visibility
submitButton.visibility =
if (displayMode.pagination.showSubmitButton) View.VISIBLE else View.GONE
reviewModeButton.visibility =
if (displayMode.pagination.showReviewButton) View.VISIBLE else View.GONE
if (displayMode.pagination.isPaginated) {
paginationPreviousButton.visibility = View.VISIBLE
paginationPreviousButton.isEnabled = displayMode.pagination.hasPreviousPage
paginationNextButton.visibility = View.VISIBLE
paginationNextButton.isEnabled = displayMode.pagination.hasNextPage
} else {
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE
}
}
is DisplayMode.InitMode -> {
paginationPreviousButton.visibility = View.GONE
paginationNextButton.visibility = View.GONE
submitButton.visibility = View.GONE
reviewModeButton.visibility = View.GONE
}
}
}

/** 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.google.android.fhir.datacapture

/** A single page in the questionnaire. This is used for the UI to render pagination controls. */
data class QuestionnairePage(
val index: Int,
val enabled: Boolean,
val hidden: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.google.android.fhir.datacapture

/**
* Pagination information of the questionnaire. This is used for the UI to render pagination
* controls. Includes information for each page and the current page index.
*/
data class QuestionnairePagination(
val isPaginated: Boolean = false,
val pages: List<QuestionnairePage>,
val currentPageIndex: Int,
val showSubmitButton: Boolean = false,
val showReviewButton: Boolean = false,
)
Loading

0 comments on commit 1c3d155

Please sign in to comment.