Skip to content

Commit

Permalink
Search use case in the demo app. (google#2754)
Browse files Browse the repository at this point in the history
* Search use case.

* Update ui edge cases.

* spotless apply.

* Address ui changes.

* Code refactoring

* address review comments.

---------

Co-authored-by: Santosh Pingle <[email protected]>
  • Loading branch information
santosh-pingle and Santosh Pingle authored Jan 8, 2025
1 parent 8d306be commit f343e26
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 Google LLC
* Copyright 2022-2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@ import android.view.inputmethod.InputMethodManager
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment
Expand All @@ -53,6 +54,35 @@ class PatientListFragment : Fragment() {
savedInstanceState: Bundle?,
): View {
_binding = FragmentPatientListBinding.inflate(inflater, container, false)

val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

binding.givenNameEditText.apply {
addTextChangedListener(
onTextChanged = { text, _, _, _ ->
patientListViewModel.setPatientGivenName(text.toString())
},
)
setOnFocusChangeListener { view, hasFocus ->
if (!hasFocus) {
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
}

binding.familyNameEditText.apply {
addTextChangedListener(
onTextChanged = { text, _, _, _ ->
patientListViewModel.setPatientFamilyName(text.toString())
},
)
setOnFocusChangeListener { view, hasFocus ->
if (!hasFocus) {
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
}

return binding.root
}

Expand Down Expand Up @@ -87,27 +117,6 @@ class PatientListFragment : Fragment() {
binding.patientListContainer.patientCount.text = "$it Patient(s)"
}

searchView = binding.search
searchView.setOnQueryTextListener(
object : SearchView.OnQueryTextListener {
override fun onQueryTextChange(newText: String): Boolean {
patientListViewModel.searchPatientsByName(newText)
return true
}

override fun onQueryTextSubmit(query: String): Boolean {
patientListViewModel.searchPatientsByName(query)
return true
}
},
)
searchView.setOnQueryTextFocusChangeListener { view, focused ->
if (!focused) {
// hide soft keyboard
(requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager)
.hideSoftInputFromWindow(view.windowToken, 0)
}
}
requireActivity()
.onBackPressedDispatcher
.addCallback(
Expand All @@ -123,7 +132,6 @@ class PatientListFragment : Fragment() {
}
},
)

setHasOptionsMenu(true)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 Google LLC
* Copyright 2023-2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,12 +39,11 @@ import org.hl7.fhir.r4.model.RiskAssessment
*/
class PatientListViewModel(application: Application, private val fhirEngine: FhirEngine) :
AndroidViewModel(application) {

val liveSearchedPatients = MutableLiveData<List<PatientItem>>()
val patientCount = MutableLiveData<Long>()

init {
updatePatientListAndPatientCount({ getSearchResults() }, { count() })
updatePatientListAndPatientCount({ getSearchResults() }, { searchedPatientCount() })
}

fun searchPatientsByName(nameQuery: String) {
Expand Down Expand Up @@ -174,6 +173,79 @@ class PatientListViewModel(application: Application, private val fhirEngine: Fhi
throw IllegalArgumentException("Unknown ViewModel class")
}
}

private var patientGivenName: String? = null
private var patientFamilyName: String? = null

fun setPatientGivenName(givenName: String) {
patientGivenName = givenName
searchPatientsByParameter()
}

fun setPatientFamilyName(familyName: String) {
patientFamilyName = familyName
searchPatientsByParameter()
}

private fun searchPatientsByParameter() {
viewModelScope.launch {
liveSearchedPatients.value = searchPatients()
patientCount.value = searchedPatientCount()
}
}

private suspend fun searchPatients(): List<PatientItem> {
val patients =
fhirEngine
.search<Patient> {
filter(
Patient.GIVEN,
{
modifier = StringFilterModifier.CONTAINS
this.value = patientGivenName ?: ""
},
)
filter(
Patient.FAMILY,
{
modifier = StringFilterModifier.CONTAINS
this.value = patientFamilyName ?: ""
},
)
sort(Patient.GIVEN, Order.ASCENDING)
count = 100
from = 0
}
.mapIndexed { index, fhirPatient -> fhirPatient.resource.toPatientItem(index + 1) }
.toMutableList()

val risks = getRiskAssessments()
patients.forEach { patient ->
risks["Patient/${patient.resourceId}"]?.let {
patient.risk = it.prediction?.first()?.qualitativeRisk?.coding?.first()?.code
}
}
return patients
}

private suspend fun searchedPatientCount(): Long {
return fhirEngine.count<Patient> {
filter(
Patient.GIVEN,
{
modifier = StringFilterModifier.CONTAINS
this.value = patientGivenName ?: ""
},
)
filter(
Patient.FAMILY,
{
modifier = StringFilterModifier.CONTAINS
this.value = patientFamilyName ?: ""
},
)
}
}
}

internal fun Patient.toPatientItem(position: Int): PatientListViewModel.PatientItem {
Expand Down
49 changes: 44 additions & 5 deletions demo/src/main/res/layout/fragment_patient_list.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,54 @@
android:focusableInTouchMode="true"
android:orientation="vertical"
>

<androidx.appcompat.widget.SearchView
android:id="@+id/search"
<!-- Label for Search -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:iconifiedByDefault="false"
app:queryHint="@string/query_hint_patient_search"
android:layout_marginHorizontal="20dp"
android:text="@string/search_patient_by"
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
/>

<!-- Horizontal layout for Given Name and Family Name -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:hintEnabled="true"
android:hint="@string/given_name"
>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/given_name_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:hintEnabled="true"
android:hint="@string/family_name"
android:layout_marginStart="10dp"
>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/family_name_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

<!-- Patient List -->
<include
android:id="@+id/patient_list_container"
layout="@layout/patient_list_view"
Expand Down
4 changes: 3 additions & 1 deletion demo/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

<!-- For display observations in brief, for a patient -->
<string name="observation_brief_text">%1$s: %2$s\nEffective: %3$s</string>
<string name="query_hint_patient_search">Find by Patient Name</string>
<string
name="cancel_questionnaire_message"
>Are you sure you want to discard the answers?</string>
Expand Down Expand Up @@ -74,4 +73,7 @@
<string name="last_sync_status">Last sync status: %1$s</string>
<string name="last_sync_status_na">Last sync status: Not available</string>
<string name="periodic_sync">Periodic sync</string>
<string name="search_patient_by">Search Patient by</string>
<string name="given_name">Given name</string>
<string name="family_name">Family name</string>
</resources>

0 comments on commit f343e26

Please sign in to comment.