1616
1717package com.google.android.fhir.datacapture.views.factories
1818
19- import android.annotation.SuppressLint
2019import android.content.Context
2120import android.text.InputType
2221import android.text.format.DateFormat
2322import android.view.View
2423import androidx.appcompat.app.AppCompatActivity
24+ import androidx.compose.foundation.layout.Column
25+ import androidx.compose.foundation.layout.fillMaxWidth
26+ import androidx.compose.foundation.layout.padding
27+ import androidx.compose.runtime.Composable
28+ import androidx.compose.runtime.getValue
29+ import androidx.compose.runtime.mutableStateOf
30+ import androidx.compose.runtime.remember
31+ import androidx.compose.runtime.rememberCoroutineScope
32+ import androidx.compose.runtime.setValue
33+ import androidx.compose.ui.Modifier
34+ import androidx.compose.ui.platform.LocalContext
35+ import androidx.compose.ui.res.dimensionResource
36+ import androidx.compose.ui.res.stringResource
2537import androidx.lifecycle.lifecycleScope
2638import com.google.android.fhir.datacapture.R
2739import com.google.android.fhir.datacapture.extensions.getRequiredOrOptionalText
40+ import com.google.android.fhir.datacapture.extensions.getValidationErrorMessage
41+ import com.google.android.fhir.datacapture.extensions.itemMedia
2842import com.google.android.fhir.datacapture.extensions.toLocalizedString
2943import com.google.android.fhir.datacapture.extensions.tryUnwrapContext
30- import com.google.android.fhir.datacapture.views.HeaderView
3144import com.google.android.fhir.datacapture.views.QuestionnaireViewItem
45+ import com.google.android.fhir.datacapture.views.compose.Header
46+ import com.google.android.fhir.datacapture.views.compose.MediaItem
47+ import com.google.android.fhir.datacapture.views.compose.TimePickerItem
3248import com.google.android.material.textfield.TextInputEditText
3349import com.google.android.material.textfield.TextInputLayout
3450import com.google.android.material.timepicker.MaterialTimePicker
@@ -37,25 +53,76 @@ import com.google.android.material.timepicker.MaterialTimePicker.INPUT_MODE_KEYB
3753import com.google.android.material.timepicker.TimeFormat
3854import java.time.LocalTime
3955import java.time.format.DateTimeFormatter
56+ import kotlinx.coroutines.Dispatchers
4057import kotlinx.coroutines.launch
4158import org.hl7.fhir.r4.model.QuestionnaireResponse
4259import org.hl7.fhir.r4.model.TimeType
4360
44- object TimePickerViewHolderFactory :
45- QuestionnaireItemAndroidViewHolderFactory (R .layout.time_picker_view) {
61+ object TimePickerViewHolderFactory : QuestionnaireItemComposeViewHolderFactory {
4662
4763 override fun getQuestionnaireItemViewHolderDelegate () =
48- object : QuestionnaireItemAndroidViewHolderDelegate {
64+ object : QuestionnaireItemComposeViewHolderDelegate {
4965 private val TAG = " time-picker"
5066 private lateinit var context: AppCompatActivity
51- private lateinit var header: HeaderView
5267 private lateinit var timeInputLayout: TextInputLayout
5368 private lateinit var timeInputEditText: TextInputEditText
54- override lateinit var questionnaireViewItem: QuestionnaireViewItem
69+ lateinit var questionnaireViewItem: QuestionnaireViewItem
5570
56- override fun init (itemView : View ) {
71+ @Composable
72+ override fun Content (questionnaireViewItem : QuestionnaireViewItem ) {
73+ val context = LocalContext .current
74+ val validationMessage =
75+ remember(questionnaireViewItem.validationResult) {
76+ getValidationErrorMessage(
77+ context,
78+ questionnaireViewItem,
79+ questionnaireViewItem.validationResult,
80+ )
81+ }
82+ val requiredOptionalText =
83+ remember(questionnaireViewItem) {
84+ getRequiredOrOptionalText(questionnaireViewItem, context)
85+ }
86+ val readOnly =
87+ remember(questionnaireViewItem.questionnaireItem) {
88+ questionnaireViewItem.questionnaireItem.readOnly
89+ }
90+ val questionnaireViewItemLocalTimeAnswer =
91+ remember(questionnaireViewItem.answers) {
92+ questionnaireViewItem.answers.singleOrNull()?.valueTimeType?.localTime
93+ }
94+ var questionnaireViewItemLocalTimeAnswerDisplay by
95+ remember(questionnaireViewItemLocalTimeAnswer) {
96+ mutableStateOf(questionnaireViewItemLocalTimeAnswer?.toLocalizedString(context))
97+ }
98+ val coroutineScope = rememberCoroutineScope { Dispatchers .Main }
99+
100+ Column (
101+ modifier =
102+ Modifier .padding(
103+ horizontal = dimensionResource(R .dimen.item_margin_horizontal),
104+ vertical = dimensionResource(R .dimen.item_margin_vertical),
105+ ),
106+ ) {
107+ Header (questionnaireViewItem)
108+ questionnaireViewItem.questionnaireItem.itemMedia?.let { MediaItem (it) }
109+ TimePickerItem (
110+ modifier = Modifier .fillMaxWidth(),
111+ selectedTime = questionnaireViewItemLocalTimeAnswerDisplay,
112+ enabled = ! readOnly,
113+ hint = stringResource(R .string.time),
114+ supportingHelperText =
115+ if (! validationMessage.isNullOrBlank()) validationMessage else requiredOptionalText,
116+ isError = ! validationMessage.isNullOrBlank(),
117+ ) {
118+ questionnaireViewItemLocalTimeAnswerDisplay = it.toLocalizedString(context)
119+ coroutineScope.launch { setQuestionnaireItemViewItemAnswer(it) }
120+ }
121+ }
122+ }
123+
124+ fun init (itemView : View ) {
57125 context = itemView.context.tryUnwrapContext()!!
58- header = itemView.findViewById(R .id.header)
59126 timeInputLayout = itemView.findViewById(R .id.text_input_layout)
60127 timeInputEditText = itemView.findViewById(R .id.text_input_edit_text)
61128 timeInputEditText.inputType = InputType .TYPE_NULL
@@ -74,31 +141,6 @@ object TimePickerViewHolderFactory :
74141 }
75142 }
76143
77- @SuppressLint(" NewApi" ) // java.time APIs can be used due to desugaring
78- override fun bind (questionnaireViewItem : QuestionnaireViewItem ) {
79- clearPreviousState()
80- header.bind(questionnaireViewItem)
81- timeInputLayout.helperText = getRequiredOrOptionalText(questionnaireViewItem, context)
82-
83- val questionnaireItemViewItemDateTimeAnswer =
84- questionnaireViewItem.answers.singleOrNull()?.valueTimeType?.localTime
85-
86- // If there is no set answer in the QuestionnaireItemViewItem, make the time field empty.
87- timeInputEditText.setText(
88- questionnaireItemViewItemDateTimeAnswer?.toLocalizedString(timeInputEditText.context)
89- ? : " " ,
90- )
91- }
92-
93- override fun setReadOnly (isReadOnly : Boolean ) {
94- // The system outside this delegate should only be able to mark it read only. Otherwise, it
95- // will change the state set by this delegate in bindView().
96- if (isReadOnly) {
97- timeInputEditText.isEnabled = false
98- timeInputLayout.isEnabled = false
99- }
100- }
101-
102144 private fun buildMaterialTimePicker (context : Context , inputMode : Int ) {
103145 val selectedTime =
104146 questionnaireViewItem.answers.singleOrNull()?.valueTimeType?.localTime ? : LocalTime .now()
@@ -109,7 +151,7 @@ object TimePickerViewHolderFactory :
109151 TimeFormat .CLOCK_12H
110152 }
111153 MaterialTimePicker .Builder ()
112- .setTitleText(R .string.select_time)
154+ // .setTitleText(R.string.select_time)
113155 .setHour(selectedTime.hour)
114156 .setMinute(selectedTime.minute)
115157 .setTimeFormat(timeFormat)
@@ -119,7 +161,10 @@ object TimePickerViewHolderFactory :
119161 addOnPositiveButtonClickListener {
120162 with (LocalTime .of(this .hour, this .minute, 0 )) {
121163 timeInputEditText.setText(this .toLocalizedString(context))
122- setQuestionnaireItemViewItemAnswer(this )
164+
165+ context.tryUnwrapContext()?.lifecycleScope?.launch {
166+ setQuestionnaireItemViewItemAnswer(this @with)
167+ }
123168 timeInputEditText.clearFocus()
124169 }
125170 }
@@ -128,18 +173,11 @@ object TimePickerViewHolderFactory :
128173 }
129174
130175 /* * Set the answer in the [QuestionnaireResponse]. */
131- private fun setQuestionnaireItemViewItemAnswer (localDateTime : LocalTime ) =
132- context.lifecycleScope.launch {
133- questionnaireViewItem.setAnswer(
134- QuestionnaireResponse .QuestionnaireResponseItemAnswerComponent ()
135- .setValue(TimeType (localDateTime.format(DateTimeFormatter .ISO_TIME ))),
136- )
137- }
138-
139- private fun clearPreviousState () {
140- timeInputEditText.isEnabled = true
141- timeInputLayout.isEnabled = true
142- }
176+ private suspend fun setQuestionnaireItemViewItemAnswer (localDateTime : LocalTime ) =
177+ questionnaireViewItem.setAnswer(
178+ QuestionnaireResponse .QuestionnaireResponseItemAnswerComponent ()
179+ .setValue(TimeType (localDateTime.format(DateTimeFormatter .ISO_TIME ))),
180+ )
143181 }
144182
145183 private val TimeType .localTime
0 commit comments