Skip to main content
GuidesAPI ReferenceExamples

Free Text Search

|

In this guide you will learn how to do a text search and display them in a list.

Search results

Search service

Inside MainActivity.kt retain a SearchService instance. Normally in Android's MVVM, services are retained in the Repository layer, injected via dependency injection, and ultimately consumed by ViewModels but for the scope of this example we will use it inside an activity. The search service has a onCompleted callback which will update the UI.

MainActivity.kt
class MainActivity : AppCompatActivity()
{
//...
private var searchService = SearchService(
onCompleted = { results, errorCode, _ ->
//...
when (errorCode)
{
GemError.NoError ->
{
// No error encountered, we can handle the results.
refreshList(results)
noResultText.isVisible = results.isEmpty()
}

GemError.Cancel ->
{
// The search action was cancelled.
}

GemError.Busy ->
{
showDialog("Requested operation cannot be performed. Internal limit reached. Please use an API token in order to avoid this error.")
}

else ->
{
// There was a problem at computing the search operation.
showDialog("Search service error: ${GemError.getMessage(errorCode)}")
}
}
//...
}
)
//...
}

Searching by filter

Initiate a new search each time the query gets updated. If there is a pre-existing search process it will be canceled. The reference point will be taken into account when calculating the relative distance of query-matching locations found during the search process. As a result, the relevance of these locations will change, and the order in which the results appear may vary depending on the reference point.

MainActivity.kt
class MainActivity : AppCompatActivity()
{
private lateinit var searchView: SearchView
//...

override fun onCreate(savedInstanceState: Bundle?)
{
searchView = findViewById(R.id.search_input)
//...
searchView.apply {
setOnQueryTextListener(
object : SearchView.OnQueryTextListener
{
override fun onQueryTextSubmit(query: String?): Boolean
{
clearFocus()
return true
}

override fun onQueryTextChange(newText: String?): Boolean {
val filter = (newText ?: "").trim()
if (filter.isNotEmpty())
progressBar.visibility = View.VISIBLE
// Search the requested filter.
search(filter)
return true
}
}
)

requestFocus()
}
//...
}
//...

private fun search(filter: String) = SdkCall.postAsync({
// Cancel any search that is in progress now.
searchService.cancelSearch()
if (filter.isBlank()) {
refreshList(arrayListOf())
noResultText.isVisible = false
}

// Give a random position if position is not available
val position = PositionService.position
reference = if (position?.isValid() == true)
position.coordinates
else
Coordinates(51.5072, 0.1276) // center London


val res = searchService.searchByFilter(filter, reference)

if (GemError.isError(res) && res != GemError.Cancel) {
showDialog(GemError.getMessage(res))
}
//...

}, 200)
//...
}

Refreshing the list

After each call to searchService.searchByFilter(filter, reference) onCompleted callback will be invoked by the SDK. If there have been no errors the received LandmarkList is mapped to SearchItem data class which holds and displays only key information. The resulted list is held in a lifecycle-aware component (LiveData) which updates the UI each time a new result list gets submitted. Finally, a ListAdapter was used for displaying the list.

MainActivity.kt
class MainActivity : AppCompatActivity() {

private val _results = MutableLiveData<List<SearchItem>>()
private val results: LiveData<List<SearchItem>> get() = _results

//...
override fun onCreate(savedInstanceState: Bundle?) {
//...
//observe the list and update UI
results.observe(this) {
customAdapter.submitList(it)
progressBar.visibility = View.GONE
}
///...
}
//...
private fun refreshList(results: ArrayList<Landmark>) = SdkCall.execute {
val list = results.map { landmark ->
val meters = reference?.let { landmark.coordinates?.getDistance(it)?.toInt() ?: 0 } ?: 0
val dist = GemUtil.getDistText(meters, EUnitSystem.Metric, true)
SearchItem(
landmark.imageAsBitmap(imageSize), landmark.name.toString(),
GemUtil.getLandmarkDescription(landmark, true),
dist.first,
dist.second
)
}
_results.postValue(list)
}

//...

/**
* UI search item data class.
*/
data class SearchItem(
val image: Bitmap? = null,
val name: String = "",
val descriptionTxt: String = "",
val distance: String = "",
val unit: String = ""
)

//...
}