Skip to content

Commit ab0e1e6

Browse files
committed
Cleanup AutoCompleteViewHolderFactory
1 parent 384dbc4 commit ab0e1e6

File tree

2 files changed

+0
-297
lines changed

2 files changed

+0
-297
lines changed

datacapture/src/main/java/com/google/android/fhir/datacapture/views/factories/AutoCompleteViewHolderFactory.kt

Lines changed: 0 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@
1616

1717
package com.google.android.fhir.datacapture.views.factories
1818

19-
import android.view.View
20-
import android.widget.AdapterView
21-
import android.widget.ArrayAdapter
22-
import android.widget.TextView
23-
import androidx.appcompat.app.AppCompatActivity
2419
import androidx.compose.foundation.layout.Column
2520
import androidx.compose.foundation.layout.fillMaxWidth
2621
import androidx.compose.foundation.layout.padding
@@ -33,47 +28,23 @@ import androidx.compose.runtime.setValue
3328
import androidx.compose.ui.Modifier
3429
import androidx.compose.ui.platform.LocalContext
3530
import androidx.compose.ui.res.dimensionResource
36-
import androidx.core.view.children
37-
import androidx.core.view.get
38-
import androidx.core.view.isEmpty
39-
import androidx.lifecycle.lifecycleScope
4031
import com.google.android.fhir.datacapture.R
4132
import com.google.android.fhir.datacapture.extensions.displayString
4233
import com.google.android.fhir.datacapture.extensions.identifierString
4334
import com.google.android.fhir.datacapture.extensions.itemMedia
44-
import com.google.android.fhir.datacapture.extensions.tryUnwrapContext
4535
import com.google.android.fhir.datacapture.validation.Invalid
46-
import com.google.android.fhir.datacapture.validation.NotValidated
47-
import com.google.android.fhir.datacapture.validation.Valid
48-
import com.google.android.fhir.datacapture.validation.ValidationResult
49-
import com.google.android.fhir.datacapture.views.HeaderView
5036
import com.google.android.fhir.datacapture.views.QuestionnaireViewItem
5137
import com.google.android.fhir.datacapture.views.compose.Header
5238
import com.google.android.fhir.datacapture.views.compose.MediaItem
5339
import com.google.android.fhir.datacapture.views.compose.MultiAutoCompleteTextItem
54-
import com.google.android.material.chip.Chip
55-
import com.google.android.material.chip.ChipGroup
56-
import com.google.android.material.textfield.MaterialAutoCompleteTextView
57-
import com.google.android.material.textfield.TextInputLayout
5840
import kotlinx.coroutines.Dispatchers
5941
import kotlinx.coroutines.launch
60-
import org.hl7.fhir.r4.model.Coding
6142
import org.hl7.fhir.r4.model.QuestionnaireResponse
6243

6344
internal object AutoCompleteViewHolderFactory : QuestionnaireItemComposeViewHolderFactory {
6445

6546
override fun getQuestionnaireItemViewHolderDelegate() =
6647
object : QuestionnaireItemComposeViewHolderDelegate {
67-
private lateinit var context: AppCompatActivity
68-
private lateinit var header: HeaderView
69-
private lateinit var autoCompleteTextView: MaterialAutoCompleteTextView
70-
private lateinit var chipContainer: ChipGroup
71-
private lateinit var textInputLayout: TextInputLayout
72-
private val canHaveMultipleAnswers
73-
get() = questionnaireViewItem.questionnaireItem.repeats
74-
75-
lateinit var questionnaireViewItem: QuestionnaireViewItem
76-
private lateinit var errorTextView: TextView
7748

7849
@Composable
7950
override fun Content(questionnaireViewItem: QuestionnaireViewItem) {
@@ -192,191 +163,5 @@ internal object AutoCompleteViewHolderFactory : QuestionnaireItemComposeViewHold
192163
)
193164
}
194165
}
195-
196-
fun init(itemView: View) {
197-
context = itemView.context.tryUnwrapContext()!!
198-
header = itemView.findViewById(R.id.header)
199-
autoCompleteTextView = itemView.findViewById(R.id.autoCompleteTextView)
200-
chipContainer = itemView.findViewById(R.id.chipContainer)
201-
textInputLayout = itemView.findViewById(R.id.text_input_layout)
202-
errorTextView = itemView.findViewById(R.id.error)
203-
autoCompleteTextView.onItemClickListener =
204-
AdapterView.OnItemClickListener { _, _, position, _ ->
205-
val answer =
206-
QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent().apply {
207-
value =
208-
questionnaireViewItem.enabledAnswerOptions
209-
.first {
210-
it.value.identifierString(header.context) ==
211-
(autoCompleteTextView.adapter.getItem(position)
212-
as AutoCompleteViewAnswerOption)
213-
.answerId
214-
}
215-
.valueCoding
216-
}
217-
218-
handleAnswerSelection(answer)
219-
autoCompleteTextView.setText("")
220-
}
221-
}
222-
223-
fun bind(questionnaireViewItem: QuestionnaireViewItem) {
224-
header.bind(questionnaireViewItem, showRequiredOrOptionalText = true)
225-
val answerOptionValues =
226-
questionnaireViewItem.enabledAnswerOptions.map {
227-
AutoCompleteViewAnswerOption(
228-
answerId = it.value.identifierString(header.context),
229-
answerDisplay = it.value.displayString(header.context),
230-
)
231-
}
232-
val adapter =
233-
ArrayAdapter(
234-
header.context,
235-
R.layout.drop_down_list_item,
236-
R.id.answer_option_textview,
237-
answerOptionValues,
238-
)
239-
autoCompleteTextView.setAdapter(adapter)
240-
// Remove chips if any from the last bindView call on this VH.
241-
chipContainer.removeAllViews()
242-
presetValuesIfAny()
243-
244-
displayValidationResult(questionnaireViewItem.validationResult)
245-
}
246-
247-
fun setReadOnly(isReadOnly: Boolean) {
248-
for (i in 0 until chipContainer.childCount) {
249-
val view = chipContainer.getChildAt(i)
250-
view.isEnabled = !isReadOnly
251-
if (view is Chip && isReadOnly) {
252-
view.setOnCloseIconClickListener(null)
253-
}
254-
}
255-
textInputLayout.isEnabled = !isReadOnly
256-
}
257-
258-
private fun presetValuesIfAny() {
259-
questionnaireViewItem.answers.map { answer -> addNewChipIfNotPresent(answer) }
260-
}
261-
262-
private fun handleAnswerSelection(
263-
answer: QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent,
264-
) {
265-
if (canHaveMultipleAnswers) {
266-
handleSelectionWhenQuestionCanHaveMultipleAnswers(answer)
267-
} else {
268-
handleSelectionWhenQuestionCanHaveSingleAnswer(answer)
269-
}
270-
}
271-
272-
/**
273-
* Adds a new chip if it not already present in [chipContainer].It returns [true] if a new
274-
* Chip is added and [false] if the Chip is already present for the selected answer. The later
275-
* will happen if the user selects an already selected answer.
276-
*/
277-
private fun addNewChipIfNotPresent(
278-
answer: QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent,
279-
): Boolean {
280-
if (chipIsAlreadyPresent(answer)) return false
281-
282-
val chip = Chip(chipContainer.context, null, R.attr.questionnaireChipStyle)
283-
chip.id = View.generateViewId()
284-
chip.text = answer.valueCoding.displayOrCode
285-
chip.isCloseIconVisible = true
286-
chip.isClickable = true
287-
chip.isCheckable = false
288-
chip.tag = answer
289-
chip.setOnCloseIconClickListener {
290-
chipContainer.removeView(chip)
291-
onChipRemoved(chip)
292-
}
293-
294-
chipContainer.addView(chip)
295-
return true
296-
}
297-
298-
private fun chipIsAlreadyPresent(
299-
answer: QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent,
300-
): Boolean {
301-
return chipContainer.children.any { chip ->
302-
(chip.tag as QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent)
303-
.value
304-
.equalsDeep(answer.value)
305-
}
306-
}
307-
308-
private fun handleSelectionWhenQuestionCanHaveSingleAnswer(
309-
answer: QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent,
310-
) {
311-
if (chipContainer.isEmpty()) {
312-
addNewChipIfNotPresent(answer)
313-
} else {
314-
(chipContainer[0] as Chip).apply {
315-
text = answer.valueCoding.displayOrCode
316-
tag = answer
317-
}
318-
}
319-
context.lifecycleScope.launch { questionnaireViewItem.setAnswer(answer) }
320-
}
321-
322-
private fun handleSelectionWhenQuestionCanHaveMultipleAnswers(
323-
answer: QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent,
324-
) {
325-
val answerNotPresent =
326-
questionnaireViewItem.answers.none { it.value.equalsDeep(answer.value) }
327-
328-
if (answerNotPresent) {
329-
addNewChipIfNotPresent(answer)
330-
context.lifecycleScope.launch { questionnaireViewItem.addAnswer(answer) }
331-
}
332-
}
333-
334-
private fun onChipRemoved(chip: Chip) {
335-
context.lifecycleScope.launch {
336-
if (canHaveMultipleAnswers) {
337-
(chip.tag as QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent).let {
338-
questionnaireViewItem.removeAnswer(it)
339-
}
340-
} else {
341-
questionnaireViewItem.clearAnswer()
342-
}
343-
}
344-
}
345-
346-
private fun displayValidationResult(validationResult: ValidationResult) {
347-
// https://github.com/material-components/material-components-android/issues/1435
348-
// Because of the above issue, we use separate error textview. But we still use
349-
// textInputLayout to show the error icon and the box color.
350-
when (validationResult) {
351-
is NotValidated,
352-
Valid, -> {
353-
errorTextView.visibility = View.GONE
354-
textInputLayout.error = null
355-
}
356-
is Invalid -> {
357-
errorTextView.text = validationResult.getSingleStringValidationMessage()
358-
errorTextView.visibility = View.VISIBLE
359-
textInputLayout.error = " " // non empty text
360-
}
361-
}
362-
}
363-
364-
private val Coding.displayOrCode: String
365-
get() =
366-
if (display.isNullOrBlank()) {
367-
code
368-
} else {
369-
display
370-
}
371166
}
372167
}
373-
374-
/**
375-
* An answer option that would show up as a dropdown item in an [AutoCompleteViewHolderFactory]
376-
* textview
377-
*/
378-
internal data class AutoCompleteViewAnswerOption(val answerId: String, val answerDisplay: String) {
379-
override fun toString(): String {
380-
return this.answerDisplay
381-
}
382-
}

datacapture/src/main/res/layout/edit_text_auto_complete_view.xml

Lines changed: 0 additions & 82 deletions
This file was deleted.

0 commit comments

Comments
 (0)