My app intends to provide nearby places suggestion when a user is inputting his address. It will also allow the user to interact with the visible map while inputting his address where necessary.
After implementing an AutoCompleteTextView with a custom ArrayAdapter backed by static data the application worked fine.
Activity
class DeliveryDestinationMapActivity : AppCompatActivity(),
OnMapReadyCallback,
DeliverySummaryFragment.Callback,
DeliverySummaryCallback, LifecycleObserver {
private lateinit var editAutoCompleteAddress: MaterialAutoCompleteTextView
private lateinit var placeAutocompleteAdapter: PlaceSampleAutoCompleteAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_delivery_destination_map)
val apiKey = BuildConfig.MAPS_API_KEY
if (apiKey.isEmpty() || (apiKey == "DEFAULT_API_KEY")) {
Log.e("Places test", "No api key")
finish()
return
}
Places.initializeWithNewPlacesApiEnabled(applicationContext, apiKey)
placesApiClient = Places.createClient(this)
placeAutocompleteAdapter = PlaceSampleAutoCompleteAdapter(this, placesApiClient)/*PlaceAutoCompleteArrayAdapter(this, placesApiClient)*/
editAutoCompleteAddress = findViewById(R.id.edit_autocomplete_address)
editAutoCompleteAddress.setAdapter(placeAutocompleteAdapter)
editAutoCompleteAddress.onItemClickListener =
AdapterView.OnItemClickListener { parent, view, position, id ->
val prediction = parent.getItemAtPosition(position) as String
val placeId = prediction
val primaryText = prediction
Log.i("API_EXCEPTION_TAG", "Autocomplete item selected: (placeId:$placeId) -> (primaryText:$primaryText)");
editAutoCompleteAddress.apply {
setText(prediction)
setSelection(editAutoCompleteAddress.length())
}
}
}
}
Custom ArrayAdapter with static data
class PlaceSampleAutoCompleteAdapter(context: Context, val placesApiClient: PlacesClient)
: ArrayAdapter<String>(context, 0), Filterable {
private val sessionToken = AutocompleteSessionToken.newInstance()
private var sampleStringResultFull = mutableListOf<String>()
override fun getCount(): Int {
return sampleStringResultFull.size
}
override fun getItem(position: Int): String? {
return sampleStringResultFull[position]
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var rowView = convertView
if (rowView == null) {
rowView = LayoutInflater.from(context).inflate(R.layout.item_place_auto_complete_prediction, parent, false)
}
val testString = getItem(position)
val textPlaceAddress = rowView?.findViewById<TextView>(R.id.text_title_address)
val textPlaceCountryState = rowView?.findViewById<TextView>(R.id.text_address_country_state)
textPlaceAddress?.text = testString
textPlaceCountryState?.text = testString
return rowView!!
}
override fun getFilter(): Filter {
return sampleStringFilter
}
private val sampleStringFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filterResults = FilterResults()
var filterData = mutableListOf<String>()
if (!constraint.isNullOrEmpty()) {
sampleStringResultFull.clear()
filterData = getAutocompleteSampleStrings(constraint)
}
filterResults.values = filterData
filterResults.count = filterData.size
return filterResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
if (results != null && results.count > 0) {
sampleStringResultFull = results.values as MutableList<String>
notifyDataSetChanged()
}
else {
notifyDataSetInvalidated()
}
}
override fun convertResultToString(resultValue: Any?): CharSequence {
if (resultValue is String) {
return resultValue
}
return super.convertResultToString(resultValue)
}
}
private fun getAutocompleteSampleStrings(constraint: CharSequence): MutableList<String> {
val remoteStrings = mutableListOf<String>("Afghanistan", "Albania", "Algeria", "Armenia", "Australia", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Côte d Ivoire", "Cabo Verde", "Cambodia", "Cameroon", "Canada", "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Grenada", "Guatemala", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Italy", "Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Montenegro", "Morocco", "Mozambique", "North Macedonia", "Norway", "Oman", "Pakistan", "Palau", "Palestine State", "Panama", "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Somalia", "South Africa", "South Korea", "South Sudan", "Spain", "Tanzania", "Thailand", "Timor-Leste", "Togo", "Tonga", "United Arab Emirates", "United Kingdom", "United States of America")
val patterns = constraint.toString().lowercase().trim()
remoteStrings.forEach { if (it.contains(patterns)) sampleStringResultFull.add(it) }
notifyDataSetChanged()
return sampleStringResultFull
}
}
This is my final implementation of the arrayadapter that i'm unable to make work. I have tried debugging the problem with a debugger line by line. I assume there is some sort of async problem.
Can anyone figure the problem.
Custom
ArrayAdapterwith remote google places predictions(function)
private fun getAutocompleteSampleStrings(constraint: CharSequence): MutableList<String> {
var remotePredictionList = mutableListOf<String>()
val request = FindAutocompletePredictionsRequest.builder()
.setTypesFilter(listOf(PlaceTypes.ADDRESS, PlaceTypes.COUNTRY, PlaceTypes.GEOCODE, PlaceTypes.CITIES))
.setSessionToken(sessionToken)
.setQuery(constraint.toString())
.build()
// Perform autocomplete predictions request
placesApiClient.findAutocompletePredictions(request)
.addOnSuccessListener { response ->
val predictions = response.autocompletePredictions
clear()
predictions.forEach { remotePredictionList.add(it.getFullText(null).toString()) }
notifyDataSetChanged()
}
.addOnFailureListener { exception ->
if (exception is com.google.android.gms.common.api.ApiException) {
val apiException = exception as com.google.android.gms.common.api.ApiException
Thread.currentThread().name
Log.e("API_EXCEPTION_TAG", "Place not found: " + apiException.message);
}
}
return remotePredictionList
}