Address Autofill + Search UI SDK + 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.
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.
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayoutxmlns: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"> <EditTextandroid: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.ConstraintLayoutandroid: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"> <EditTextandroid: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"/> <EditTextandroid: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"/> <EditTextandroid: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"/> <EditTextandroid: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"/> <TextViewandroid: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"/> <TextViewandroid: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.MapViewandroid: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"/> <ImageViewandroid: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"/> <Spaceandroid: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.SearchResultsViewandroid: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>
package com.mapbox.search.sample import android.Manifestimport android.os.Bundleimport android.text.Editableimport android.text.TextWatcherimport android.view.Viewimport android.widget.EditTextimport android.widget.TextViewimport androidx.appcompat.app.AppCompatActivityimport androidx.core.app.ActivityCompatimport androidx.core.view.isVisibleimport androidx.lifecycle.lifecycleScopeimport com.mapbox.android.core.location.LocationEngineProviderimport com.mapbox.geojson.Pointimport com.mapbox.maps.CameraOptionsimport com.mapbox.maps.MapViewimport com.mapbox.maps.MapboxMapimport com.mapbox.maps.Styleimport com.mapbox.search.autofill.AddressAutofillimport com.mapbox.search.autofill.AddressAutofillOptionsimport com.mapbox.search.autofill.AddressAutofillResultimport com.mapbox.search.autofill.AddressAutofillSuggestionimport com.mapbox.search.autofill.Queryimport com.mapbox.search.ui.adapter.autofill.AddressAutofillUiAdapterimport com.mapbox.search.ui.view.CommonSearchViewConfigurationimport com.mapbox.search.ui.view.DistanceUnitTypeimport com.mapbox.search.ui.view.SearchResultsView class AddressAutofillUiActivity : AppCompatActivity() { private lateinit var addressAutofill: AddressAutofill private lateinit var searchResultsView: SearchResultsViewprivate lateinit var addressAutofillUiAdapter: AddressAutofillUiAdapter private lateinit var queryEditText: EditText private lateinit var apartmentEditText: EditTextprivate lateinit var cityEditText: EditTextprivate lateinit var stateEditText: EditTextprivate lateinit var zipEditText: EditTextprivate lateinit var fullAddress: TextViewprivate lateinit var pinCorrectionNote: TextViewprivate lateinit var mapView: MapViewprivate lateinit var mapPin: Viewprivate lateinit var mapboxMap: MapboxMap private var ignoreNextMapIdleEvent: Boolean = falseprivate 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 = falsereturn@addOnMapIdleListener} val mapCenter = mapboxMap.cameraState.centerfindAddress(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 = falsereturn} 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.addresscityEditText.setText(address.place)stateEditText.setText(address.region)zipEditText.setText(address.postcode) fullAddress.isVisible = truefullAddress.text = result.suggestion.formattedAddress pinCorrectionNote.isVisible = true if (!fromReverseGeocoding) {mapView.getMapboxMap().setCamera(CameraOptions.Builder().center(result.suggestion.coordinate).zoom(16.0).build())ignoreNextMapIdleEvent = truemapPin.isVisible = true} ignoreNextQueryTextUpdate = truequeryEditText.setText(listOfNotNull(address.houseNumber,address.street).joinToString())queryEditText.clearFocus() searchResultsView.isVisible = falsesearchResultsView.hideKeyboard()} private companion object {const val PERMISSIONS_REQUEST_LOCATION = 0}}