Skip to main content

Address Autofill + Search UI + Maps SDK

Note

This example is a part of the Search SDK for Android sample app. You can find the values for all referenced resources in the res directory. For example, see res/values/strings.xml for R.string.* references used in this example. The dependencies can be found here.The examples use View binding.See setup documention if necessary.

Besides Address Autofill this example also uses utility class SampleAppUtils.kt and Mapbox Maps SDK. You need to add it as a dependency using the Maps SDK v10 installation instructions.

activity_address_autofill
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/windowBackground"
tools:context=".AddressAutofillUiActivity"
>

<EditText
android:id="@+id/query_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:autofillHints="@null"
android:background="@drawable/card_background"
android:elevation="4dp"
android:hint="@string/address_autofill_query_hint"
android:inputType="text"
android:minHeight="?actionBarSize"
android:paddingHorizontal="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>

<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:background="@drawable/card_background"
android:elevation="4dp"
android:padding="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/query_text"
>

<EditText
android:id="@+id/address_city"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autofillHints="postalAddress"
android:hint="@string/address_autofill_address_city_hint"
android:inputType="textPostalAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>

<EditText
android:id="@+id/address_state"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autofillHints="postalAddress"
android:hint="@string/address_autofill_address_state_hint"
android:inputType="textPostalAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/address_city"
/>

<EditText
android:id="@+id/address_zip"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autofillHints="postalAddress"
android:hint="@string/address_autofill_address_zip_hint"
android:inputType="number"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/address_state"
/>

<EditText
android:id="@+id/address_apartment"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:autofillHints="postalAddress"
android:hint="@string/address_autofill_address_apt_hint"
android:inputType="textPostalAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/address_zip"
/>

<TextView
android:id="@+id/full_address"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="16dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/address_apartment"
tools:visibility="visible"
/>

<TextView
android:id="@+id/pin_correction_note"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:visibility="gone"
android:text="@string/address_autofill_pin_correction_note"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/full_address"
tools:visibility="visible"
/>

<com.mapbox.maps.MapView
android:id="@+id/map"
android:layout_width="0dp"
android:layout_height="150dp"
android:layout_margin="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pin_correction_note"
/>

<ImageView
android:id="@+id/map_pin"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:adjustViewBounds="true"
android:src="@drawable/red_marker"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/map_center"
app:layout_constraintEnd_toEndOf="@+id/map"
app:layout_constraintStart_toStartOf="@+id/map"
/>

<Space
android:id="@+id/map_center"
android:layout_width="1dp"
android:layout_height="1dp"
app:layout_constraintBottom_toBottomOf="@+id/map"
app:layout_constraintEnd_toEndOf="@+id/map"
app:layout_constraintStart_toStartOf="@+id/map"
app:layout_constraintTop_toTopOf="@+id/map"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

<com.mapbox.search.ui.view.SearchResultsView
android:id="@+id/search_results_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
android:background="@drawable/card_background"
android:clipToPadding="false"
android:elevation="4dp"
android:paddingTop="8dp"
android:paddingBottom="22dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/query_text"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
AddressAutofillUiActivity.kt
package com.mapbox.search.sample

import android.Manifest
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.mapbox.android.core.location.LocationEngineProvider
import com.mapbox.geojson.Point
import com.mapbox.maps.CameraOptions
import com.mapbox.maps.MapView
import com.mapbox.maps.MapboxMap
import com.mapbox.maps.Style
import com.mapbox.search.autofill.AddressAutofill
import com.mapbox.search.autofill.AddressAutofillOptions
import com.mapbox.search.autofill.AddressAutofillResult
import com.mapbox.search.autofill.AddressAutofillSuggestion
import com.mapbox.search.autofill.Query
import com.mapbox.search.ui.adapter.autofill.AddressAutofillUiAdapter
import com.mapbox.search.ui.view.CommonSearchViewConfiguration
import com.mapbox.search.ui.view.DistanceUnitType
import com.mapbox.search.ui.view.SearchResultsView

class AddressAutofillUiActivity : AppCompatActivity() {

private lateinit var addressAutofill: AddressAutofill

private lateinit var searchResultsView: SearchResultsView
private lateinit var addressAutofillUiAdapter: AddressAutofillUiAdapter

private lateinit var queryEditText: EditText

private lateinit var apartmentEditText: EditText
private lateinit var cityEditText: EditText
private lateinit var stateEditText: EditText
private lateinit var zipEditText: EditText
private lateinit var fullAddress: TextView
private lateinit var pinCorrectionNote: TextView
private lateinit var mapView: MapView
private lateinit var mapPin: View
private lateinit var mapboxMap: MapboxMap

private var ignoreNextMapIdleEvent: Boolean = false
private var ignoreNextQueryTextUpdate: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_address_autofill)

addressAutofill = AddressAutofill.create(getString(R.string.mapbox_access_token))

queryEditText = findViewById(R.id.query_text)
apartmentEditText = findViewById(R.id.address_apartment)
cityEditText = findViewById(R.id.address_city)
stateEditText = findViewById(R.id.address_state)
zipEditText = findViewById(R.id.address_zip)
fullAddress = findViewById(R.id.full_address)
pinCorrectionNote = findViewById(R.id.pin_correction_note)

mapPin = findViewById(R.id.map_pin)
mapView = findViewById(R.id.map)
mapboxMap = mapView.getMapboxMap()
mapboxMap.loadStyleUri(Style.MAPBOX_STREETS)
mapboxMap.addOnMapIdleListener {
if (ignoreNextMapIdleEvent) {
ignoreNextMapIdleEvent = false
return@addOnMapIdleListener
}

val mapCenter = mapboxMap.cameraState.center
findAddress(mapCenter)
}

searchResultsView = findViewById(R.id.search_results_view)

searchResultsView.initialize(
SearchResultsView.Configuration(
commonConfiguration = CommonSearchViewConfiguration(DistanceUnitType.IMPERIAL)
)
)

addressAutofillUiAdapter = AddressAutofillUiAdapter(
view = searchResultsView,
addressAutofill = addressAutofill
)

LocationEngineProvider.getBestLocationEngine(applicationContext).lastKnownLocation(this) { point ->
point?.let {
mapView.getMapboxMap().setCamera(
CameraOptions.Builder()
.center(point)
.zoom(9.0)
.build()
)
ignoreNextMapIdleEvent = true
}
}

addressAutofillUiAdapter.addSearchListener(object : AddressAutofillUiAdapter.SearchListener {

override fun onSuggestionSelected(suggestion: AddressAutofillSuggestion) {
selectSuggestion(
suggestion,
fromReverseGeocoding = false,
)
}

override fun onSuggestionsShown(suggestions: List<AddressAutofillSuggestion>) {
// Nothing to do
}

override fun onError(e: Exception) {
// Nothing to do
}
})

queryEditText.addTextChangedListener(object : TextWatcher {

override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {
if (ignoreNextQueryTextUpdate) {
ignoreNextQueryTextUpdate = false
return
}

val query = Query.create(text.toString())
if (query != null) {
lifecycleScope.launchWhenStarted {
addressAutofillUiAdapter.search(query)
}
}
searchResultsView.isVisible = query != null
}

override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
// Nothing to do
}

override fun afterTextChanged(s: Editable) {
// Nothing to do
}
})

if (!isPermissionGranted(Manifest.permission.ACCESS_FINE_LOCATION)) {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
),
PERMISSIONS_REQUEST_LOCATION
)
}
}

private fun findAddress(point: Point) {
lifecycleScope.launchWhenStarted {
val response = addressAutofill.suggestions(point, AddressAutofillOptions())
response.onValue { suggestions ->
if (suggestions.isEmpty()) {
showToast(R.string.address_autofill_error_pin_correction)
} else {
selectSuggestion(
suggestions.first(),
fromReverseGeocoding = true
)
}
}.onError {
showToast(R.string.address_autofill_error_pin_correction)
}
}
}

private fun selectSuggestion(suggestion: AddressAutofillSuggestion, fromReverseGeocoding: Boolean) {
lifecycleScope.launchWhenStarted {
val response = addressAutofill.select(suggestion)
response.onValue { result ->
showAddressAutofillResult(result, fromReverseGeocoding)
}.onError {
showToast(R.string.address_autofill_error_select)
}
}
}

private fun showAddressAutofillResult(result: AddressAutofillResult, fromReverseGeocoding: Boolean) {
val address = result.address
cityEditText.setText(address.place)
stateEditText.setText(address.region)
zipEditText.setText(address.postcode)

fullAddress.isVisible = true
fullAddress.text = result.suggestion.formattedAddress

pinCorrectionNote.isVisible = true

if (!fromReverseGeocoding) {
mapView.getMapboxMap().setCamera(
CameraOptions.Builder()
.center(result.suggestion.coordinate)
.zoom(16.0)
.build()
)
ignoreNextMapIdleEvent = true
mapPin.isVisible = true
}

ignoreNextQueryTextUpdate = true
queryEditText.setText(
listOfNotNull(
address.houseNumber,
address.street
).joinToString()
)
queryEditText.clearFocus()

searchResultsView.isVisible = false
searchResultsView.hideKeyboard()
}

private companion object {
const val PERMISSIONS_REQUEST_LOCATION = 0
}
}
Was this example helpful?