Skip to content

Commit

Permalink
Add Search in location picker (#71)
Browse files Browse the repository at this point in the history
* update location picker

* spotless clean
  • Loading branch information
sevenreup authored Jun 30, 2024
1 parent 09c71ed commit b81976d
Show file tree
Hide file tree
Showing 9 changed files with 411 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@ import android.text.Editable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AutoCompleteTextView
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.cardview.widget.CardView
import androidx.core.widget.doAfterTextChanged
import androidx.lifecycle.LifecycleCoroutineScope
import com.google.android.fhir.datacapture.views.HeaderView
import com.google.android.material.textfield.MaterialAutoCompleteTextView
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.launch
Expand All @@ -38,6 +37,8 @@ import org.smartregister.fhircore.engine.R
import org.smartregister.fhircore.engine.domain.model.LocationHierarchy
import org.smartregister.fhircore.engine.ui.questionnaire.items.CustomQuestItemDataProvider
import org.smartregister.fhircore.engine.ui.questionnaire.items.LocationPickerViewHolderFactory
import org.smartregister.fhircore.engine.ui.questionnaire.items.select.CustomTextView
import org.smartregister.fhircore.engine.ui.questionnaire.items.select.SelectedOption
import timber.log.Timber

class LocationPickerView(
Expand All @@ -47,7 +48,7 @@ class LocationPickerView(
) {
private var customQuestItemDataProvider: CustomQuestItemDataProvider? = null
private var rootLayout: LinearLayout? = null
private val dropdownMap = mutableMapOf<String, Pair<Int, AutoCompleteTextView>>()
private val dropdownMap = mutableMapOf<String, Pair<Int, CustomTextView<LocationHierarchy>>>()
private val dropDownLevel = mutableListOf<Int>()

private var selectedHierarchy: LocationData? = null
Expand Down Expand Up @@ -159,48 +160,47 @@ class LocationPickerView(
) {
rootLayout?.let { rootLayout ->
val mainLayout =
LayoutInflater.from(context).inflate(R.layout.custom_material_spinner, rootLayout, false)
CustomTextView<LocationHierarchy>(
context = context,
transformItem = { SelectedOption(title = it.name, id = it.identifier, item = it) },
)
mainLayout.id = View.generateViewId()
val layoutParams =
LinearLayout.LayoutParams(
FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
layoutParams.bottomMargin = 16
mainLayout.layoutParams = layoutParams

val dropdown = mainLayout.findViewById<MaterialAutoCompleteTextView>(R.id.menu_auto_complete)

if (parent != null) {
val helperText = mainLayout.findViewById<TextView>(R.id.helper_text)
helperText.visibility = View.VISIBLE
helperText.text = context.getString(R.string.select_locations_in, parent.name)
}

val adapter = LocationHierarchyAdapter(context, locations)
dropdown.setAdapter(adapter)
mainLayout.setItems(locations)

dropdown.setOnItemClickListener { _, _, position, _ ->
val selectedLocation = adapter.getItem(position)
onOptionSelected(selectedLocation, mainLayout.id, dropdown)
mainLayout.onItemClickListener = { selectedLocation ->
onOptionSelected(selectedLocation, mainLayout.id, mainLayout)
}

rootLayout.addView(mainLayout)
dropDownLevel.add(mainLayout.id)

if (parent != null) {
dropdownMap[parent.identifier] = Pair(mainLayout.id, dropdown)
dropdownMap[parent.identifier] = Pair(mainLayout.id, mainLayout)
} else {
dropdownMap["-1"] = Pair(mainLayout.id, dropdown)
dropdownMap["-1"] = Pair(mainLayout.id, mainLayout)
}

if (locations.size == 1) {
val selected = locations.first()
dropdown.setText(selected.name, false)
onOptionSelected(selected, mainLayout.id, dropdown)
dropdownMap[selected.identifier] = Pair(mainLayout.id, dropdown)
mainLayout.setTitle(selected.name, selected)
onOptionSelected(selected, mainLayout.id, mainLayout)
dropdownMap[selected.identifier] = Pair(mainLayout.id, mainLayout)
if (isDefault) {
dropdown.isEnabled = false
mainLayout.toggleEnable(false)
}
}
}
Expand All @@ -209,7 +209,7 @@ class LocationPickerView(
private fun onOptionSelected(
selectedLocation: LocationHierarchy?,
layoutId: Int,
dropdown: AutoCompleteTextView,
dropdown: CustomTextView<LocationHierarchy>,
) {
val dropIndex = dropDownLevel.indexOf(layoutId)
if (dropIndex != -1 && dropIndex != dropDownLevel.size - 1) {
Expand All @@ -224,15 +224,8 @@ class LocationPickerView(
}
}
}
val identifier = selectedLocation?.identifier
if (selectedLocation != null && selectedLocation.children.isNotEmpty()) {
if (dropdownMap.containsKey(identifier)) {
(dropdownMap[identifier]?.second?.adapter as LocationHierarchyAdapter?)?.updateLocations(
selectedLocation.children,
)
} else {
updateLocationData(selectedLocation.children, parent = selectedLocation)
}
updateLocationData(selectedLocation.children, parent = selectedLocation)
} else if (selectedLocation != null) {
this.selectedHierarchy = LocationData.fromHierarchy(selectedLocation)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright 2021 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.smartregister.fhircore.engine.ui.questionnaire.items.select

import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.TextView
import com.google.android.fhir.datacapture.extensions.tryUnwrapContext
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import org.smartregister.fhircore.engine.R

class CustomTextView<T>
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
private val transformItem: ((T) -> SelectedOption<T>)? = null,
) : FrameLayout(context, attrs) {

private var textInputLayout: TextInputLayout
private var textInputEditText: TextInputEditText
private var titleTextView: TextView

private var items: List<T> = listOf()
private var selectedOption: T? = null
var onItemClickListener: ((T) -> Unit)? = null

init {
LayoutInflater.from(context).inflate(R.layout.custom_material_spinner, this, true)

textInputLayout = findViewById(R.id.textInputLayout)
textInputEditText = findViewById(R.id.textInputEditText)
titleTextView = findViewById(R.id.helper_text)

textInputEditText.isFocusable = false
textInputEditText.isClickable = true

textInputEditText.setOnClickListener { showSearchableDialog() }
}

private fun showSearchableDialog() {
val activity =
requireNotNull(context.tryUnwrapContext()) {
"Can only use dialog select in an AppCompatActivity context"
}
val fragment =
SearchableSelectDialogFragment(
title = titleTextView.text,
selectedOptions = items.map { transformItem?.invoke(it) ?: defaultItemTransform(it) },
) {
selectedOption = it?.item
selectedOption?.let { it1 -> onSelected(it1) }
}
fragment.show(activity.supportFragmentManager, null)
}

private fun onSelected(item: T) {
textInputEditText.setText((transformItem?.invoke(item) ?: defaultItemTransform(item)).title)
onItemClickListener?.invoke(item)
}

private fun defaultItemTransform(item: T): SelectedOption<T> {
return SelectedOption(item.toString(), item.hashCode().toString(), item)
}

fun setItems(locations: List<T>) {
items = locations
}

fun setTitle(name: String, selectedItem: T? = null) {
titleTextView.text = name
if (selectedItem != null) {
textInputEditText.setText(
(transformItem?.invoke(selectedItem) ?: defaultItemTransform(selectedItem)).title,
)
}
}

fun toggleEnable(enabled: Boolean) {
textInputLayout.isEnabled = enabled
textInputEditText.isEnabled = enabled
}
}
Loading

0 comments on commit b81976d

Please sign in to comment.