All docschevron-rightSearch SDK for Androidchevron-rightarrow-leftExampleschevron-rightAddress Autofill + Search UI SDK + Maps SDK

Address Autofill + Search UI SDK + Maps SDK

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

Besides Address Autofill SDK 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.util.Log
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.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) {
showAddressAutofillSuggestion(
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 {
showAddressAutofillSuggestion(
suggestions.first(),
fromReverseGeocoding = true
)
}
}.onError { error ->
Log.d("Test.", "Test. $error", error)
showToast(R.string.address_autofill_error_pin_correction)
}
}
}
private fun showAddressAutofillSuggestion(suggestion: AddressAutofillSuggestion, fromReverseGeocoding: Boolean) {
val address = suggestion.result().address
cityEditText.setText(address.place)
stateEditText.setText(address.region)
zipEditText.setText(address.postcode)
fullAddress.isVisible = true
fullAddress.text = suggestion.formattedAddress
pinCorrectionNote.isVisible = true
if (!fromReverseGeocoding) {
mapView.getMapboxMap().setCamera(
CameraOptions.Builder()
.center(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
}
}