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.

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.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.mapbox.common.location.Location
import com.mapbox.common.location.LocationServiceFactory
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)

// Set your Access Token here if it's not already set in some other way
// MapboxOptions.accessToken = "<my-access-token>"
addressAutofill = AddressAutofill.create()

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.mapboxMap

mapboxMap.loadStyle(Style.MAPBOX_STREETS)
mapboxMap.subscribeMapIdle {
if (ignoreNextMapIdleEvent) {
ignoreNextMapIdleEvent = false
return@subscribeMapIdle
}

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
)
fun Location.toPoint(): Point = Point.fromLngLat(longitude, latitude)
val locationService = LocationServiceFactory.getOrCreate()
.getDeviceLocationProvider(null)
.value
locationService?.getLastLocation { location ->
location?.toPoint()?.let {
mapView.mapboxMap.setCamera(
CameraOptions.Builder()
.center(it)
.zoom(9.0)
.build()
)
ignoreNextMapIdleEvent = true
}
}

addressAutofillUiAdapter.addSearchListener(object : AddressAutofillUiAdapter.SearchListener {

override fun onSuggestionSelected(suggestion: AddressAutofillSuggestion) {
selectSuggestion(suggestion)
}

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 Context.showToast(@StringRes resId: Int): Unit = Toast.makeText(this, resId, Toast.LENGTH_LONG).show()

private fun Context.isPermissionGranted(permission: String): Boolean =
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED

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

private fun selectSuggestion(suggestion: AddressAutofillSuggestion) {
lifecycleScope.launchWhenStarted {
val response = addressAutofill.select(suggestion)
response.onValue { result ->
showAddressAutofillResult(result, fromReverseGeocoding = false)
}.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.mapboxMap.setCamera(
CameraOptions.Builder()
.center(result.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 fun View.hideKeyboard() =
(getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(windowToken, 0)

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