Inizia a utilizzare l'SDK Places per Android (Kotlin)

1. Prima di iniziare

Questo codelab ti insegna a integrare Places SDK per Android nella tua app e a utilizzare ciascuna delle funzionalità di Places SDK.

App demo Places

Prerequisiti

  • Conoscenza di base di Kotlin e dello sviluppo per Android

Obiettivi didattici

  • Come installare Places SDK for Android con le estensioni Kotlin.
  • Come caricare i dettagli di un luogo specifico.
  • Come aggiungere un widget Place Autocomplete alla tua app.
  • Come caricare il luogo attuale in base alla posizione attualmente segnalata del dispositivo.

Che cosa ti serve

Per completare questo codelab, ti serviranno i seguenti account, servizi e strumenti:

2. Configurazione

Per il passaggio di attivazione riportato di seguito, abilita l'API Places e l'SDK Maps per Android.

Configurare Google Maps Platform

Se non hai ancora un account Google Cloud Platform e un progetto con la fatturazione abilitata, consulta la guida Guida introduttiva a Google Maps Platform per creare un account di fatturazione e un progetto.

  1. Nella console Cloud, fai clic sul menu a discesa del progetto e seleziona il progetto che vuoi utilizzare per questo codelab.

  1. Abilita le API e gli SDK di Google Maps Platform richiesti per questo codelab in Google Cloud Marketplace. Per farlo, segui i passaggi descritti in questo video o in questa documentazione.
  2. Genera una chiave API nella pagina Credenziali di Cloud Console. Puoi seguire i passaggi descritti in questo video o in questa documentazione. Tutte le richieste a Google Maps Platform richiedono una chiave API.

3. Avvio rapido

Per iniziare il più rapidamente possibile, scarica il codice iniziale per seguire questo codelab. Puoi passare direttamente alla soluzione, ma se vuoi seguire tutti i passaggi per crearla da solo, continua a leggere.

  1. Clona il repository se hai installato git.
git clone https://github.com/googlemaps/codelab-places-101-android-kotlin.git

In alternativa, fai clic su questo pulsante per scaricare il codice sorgente.

  1. Dopo aver scaricato il codice, apri il progetto che si trova nella directory /starter in Android Studio. Questo progetto include la struttura di base dei file necessaria per completare il codelab. Tutto ciò che ti serve per lavorare si trova nella directory /starter.

Se vuoi vedere il codice della soluzione completo in esecuzione, puoi visualizzarlo nella directory /solution.

4. Aggiungere la chiave API al progetto

Questa sezione descrive come archiviare la chiave API in modo che possa essere referenziata in modo sicuro dalla tua app. Non devi archiviare la chiave API nel sistema di controllo delle versioni, pertanto ti consigliamo di archiviarla nel file secrets.properties, che verrà inserito nella copia locale della directory principale del progetto. Per ulteriori informazioni sul file secrets.properties, consulta File delle proprietà di Gradle.

Per semplificare questa attività, ti consigliamo di utilizzare il plug-in Secrets Gradle per Android.

Per installare il plug-in Secrets Gradle per Android nel tuo progetto Google Maps:

  1. In Android Studio, apri il file build.gradle.kts o build.gradle di primo livello e aggiungi il seguente codice all'elemento dependencies sotto buildscript.

Se utilizzi build.gradle.kts, aggiungi:

buildscript {
    dependencies {
        classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1")
    }
}

Se utilizzi build.gradle, aggiungi:

buildscript {
    dependencies {
        classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
    }
}
  1. Apri il file build.gradle.kts o build.gradle a livello di modulo e aggiungi il seguente codice all'elemento plugins.

Se utilizzi build.gradle.kts, aggiungi:

plugins {
    // ...
    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
}

Se utilizzi build.gradle, aggiungi:

plugins {
    // ...
    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
  1. Nel file build.gradle.kts o build.gradle a livello di modulo, assicurati che targetSdk e compileSdk siano impostati su 34.
  2. Salva il file e sincronizza il progetto con Gradle.
  3. Apri il file secrets.properties nella directory di primo livello, quindi aggiungi il codice seguente. Sostituisci YOUR_API_KEY con la tua chiave API. Memorizza la chiave in questo file perché secrets.properties è escluso dal controllo in un sistema di controllo delle versioni.
PLACES_API_KEY=YOUR_API_KEY
  1. Salva il file.
  2. Crea il file local.defaults.properties nella directory di primo livello, la stessa cartella del file secrets.properties, quindi aggiungi il seguente codice.
PLACES_API_KEY=DEFAULT_API_KEY

Lo scopo di questo file è fornire una posizione di backup per la chiave API se il file secrets.properties non viene trovato, in modo che le build non non vadano a buon fine. Ciò può accadere se cloni l'app da un sistema di controllo delle versioni che omette secrets.properties e non hai ancora creato un file secrets.properties in locale per fornire la chiave API.

  1. Salva il file.
  2. In Android Studio, apri il file build.gradle.kts o build.gradle a livello di modulo e modifica la proprietà secrets. Se la proprietà secrets non esiste, aggiungila.

Modifica le proprietà del plug-in per impostare propertiesFileName su secrets.properties, defaultPropertiesFileName su local.defaults.properties e qualsiasi altra proprietà.

secrets {
    // Optionally specify a different file name containing your secrets.
    // The plugin defaults to "local.properties"
    propertiesFileName = "secrets.properties"

    // A properties file containing default secret values. This file can be
    // checked in version control.
    defaultPropertiesFileName = "local.defaults.properties"
}

5. Installa Places SDK per Android

In questa sezione aggiungi Places SDK for Android alle dipendenze della tua app.

  1. Ora che è possibile accedere alla chiave API all'interno dell'app, aggiungi la dipendenza di Places SDK for Android al file build.gradle dell'app.

Modifica il file build.gradle a livello di app per aggiungere la dipendenza per Places SDK for Android:

build.gradle a livello di app

dependencies {
   // Dependency to include Places SDK for Android
   implementation 'com.google.android.libraries.places:places:3.4.0'
}
  1. Esegui l'app.

Ora dovresti vedere un'app con una schermata vuota. Continua a compilare questa schermata con tre demo.

6. Installa Places Android KTX

Per le app Kotlin che utilizzano uno o più SDK Android di Google Maps Platform, le librerie di estensione Kotlin (KTX) ti consentono di sfruttare le funzionalità del linguaggio Kotlin, come coroutine, proprietà/funzioni di estensione e altro ancora. Ogni SDK Google Maps ha una libreria KTX corrispondente, come mostrato di seguito:

Diagramma KTX di Google Maps Platform

In questa attività, utilizza la libreria Places Android KTX per utilizzare le funzionalità specifiche di Kotlin nella tua app.

Aggiungere la dipendenza Places Android KTX

Per sfruttare le funzionalità specifiche di Kotlin, includi la libreria KTX corrispondente per questo SDK nel file build.gradle a livello di app.

build.gradle

dependencies {
    // ...

    // Places SDK for Android KTX Library
    implementation 'com.google.maps.android:places-ktx:3.1.1'
}

7. Inizializza il client Places

Inizializza Places SDK per l'ambito dell'applicazione

All'interno del file DemoApplication.kt della cartella app/src/main/java/com/google/codelabs/maps/placesdemo, inizializza Places SDK per Android. Incolla le righe riportate di seguito alla fine della funzione onCreate:

        // Initialize the SDK with the Google Maps Platform API key
        Places.initialize(this, BuildConfig.PLACES_API_KEY)

Quando crei l'app, il plug-in Secrets Gradle per Android rende disponibile la chiave API nel file secrets.properties come BuildConfig.PLACES_API_KEY.

Aggiungere il file dell'applicazione al manifest

Poiché hai esteso Application con DemoApplication, devi aggiornare il manifest. Aggiungi la proprietà android:name all'elemento application nel file AndroidManifest.xml, che si trova in app/src/main:

    <application
        android:name=".DemoApplication"
        ...
    </application>

Questo codice indirizza il manifest dell'applicazione alla classe DemoApplication nella cartella src/main/java/com/google/codelabs/maps/placesdemo/.

8. Recupera dettagli luogo

Creare una schermata Dettagli

Un layout activity_details.xml con un LinearLayout vuoto è disponibile nella cartella app/src/main/res/layout/. Compila il layout lineare aggiungendo il seguente codice tra le parentesi <LinearLayout>.

    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/details_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/details_input_hint"
            android:text="@string/details_input_default" />

    </com.google.android.material.textfield.TextInputLayout>

    <Button
        android:id="@+id/details_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/button_details" />

    <TextView
        android:id="@+id/details_response_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:textIsSelectable="true" />

Questo codice aggiunge un campo di input di testo in cui l'utente può inserire qualsiasi ID luogo o utilizzare il valore predefinito fornito, un pulsante per avviare la richiesta di dettagli sul luogo e una TextView per visualizzare le informazioni della risposta. Le stringhe associate sono definite per te nel file src/main/res/values/strings.xml.

Creare un'attività Dettagli

  1. Crea un file DetailsActivity.kt nella cartella src/main/java/com/google/codelabs/maps/placesdemo/ e associalo al layout appena creato. Incolla questo codice nel file:
@ExperimentalCoroutinesApi
class DetailsActivity : AppCompatActivity() {
    private lateinit var placesClient: PlacesClient
    private lateinit var detailsButton: Button
    private lateinit var detailsInput: TextInputEditText
    private lateinit var responseView: TextView

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

        // Set up view objects
        detailsInput = findViewById(R.id.details_input)
        detailsButton = findViewById(R.id.details_button)
        responseView = findViewById(R.id.details_response_content)

        val apiKey = BuildConfig.PLACES_API_KEY

        // Log an error if apiKey is not set.
        if (apiKey.isEmpty() || apiKey == "DEFAULT_API_KEY") {
            Log.e(TAG, "No api key")
            finish()
            return
        }
    }
}
  1. Crea un client Places da utilizzare con questa attività. Incolla questo codice dopo il codice per controllare la chiave API nella funzione onCreate.
        // Retrieve a PlacesClient (previously initialized - see DemoApplication)
        placesClient = Places.createClient(this)
  1. Dopo aver configurato il client Places, collega un listener di clic al pulsante. Incolla questo codice dopo la creazione del client Places nella funzione onCreate.
        // Upon button click, fetch and display the Place Details
        detailsButton.setOnClickListener { button ->
            button.isEnabled = false
            val placeId = detailsInput.text.toString()
            val placeFields = listOf(
                Place.Field.NAME,
                Place.Field.ID,
                Place.Field.LAT_LNG,
                Place.Field.ADDRESS
            )
            lifecycleScope.launch {
                try {
                    val response = placesClient.awaitFetchPlace(placeId, placeFields)
                    responseView.text = response.prettyPrint()
                } catch (e: Exception) {
                    e.printStackTrace()
                    responseView.text = e.message
                }
                button.isEnabled = true
            }
        }

Questo codice recupera l'ID luogo inserito nel campo di input, definisce i campi da richiedere per il luogo, crea un FetchPlaceRequest, avvia l'attività e ascolta la risposta in caso di esito positivo o negativo. Se la richiesta ha esito positivo, la funzione compila TextView con i dettagli richiesti.

Aggiungere l'attività Dettagli al file manifest

Aggiungi un elemento <activity> per DetailsActivity come elemento secondario dell'elemento <application> nel file AndroidManifest.xml, che si trova in app/src/main:

        <activity android:name=".DetailsActivity" android:label="@string/details_demo_title" />

Aggiungere l'attività Dettagli al menu della demo

Viene fornito un modulo Demo vuoto per elencare le demo disponibili nella schermata Home. Ora che hai creato un'attività Dettagli luogo, aggiungila al file Demo.kt nella cartella src/main/java/com/google/codelabs/maps/placesdemo/ con questo codice:

    DETAILS_FRAGMENT_DEMO(
        R.string.details_demo_title,
        R.string.details_demo_description,
        DetailsActivity::class.java
    ),

Le stringhe associate sono definite nel file src/main/res/values/strings.xml.

Ispeziona MainActivity.kt e osserva che crea una ListView popolata dall'iterazione dei contenuti del modulo Demo. Se l'utente tocca un elemento nell'elenco, il listener di clic apre l'attività associata.

Esegui l'app

  1. Esegui l'app. Questa volta dovresti vedere un elemento nell'elenco che presenta la demo dei dettagli del luogo.
  2. Tocca il testo Dettagli del luogo. Dovresti visualizzare la visualizzazione che hai creato con un campo di immissione e un pulsante.
  3. Tocca il pulsante "SCOPRI DI PIÙ". Se hai utilizzato l'ID luogo predefinito, dovresti visualizzare il nome del luogo, l'indirizzo e le coordinate della mappa, come mostrato nella Figura 1.

Attività Dettagli del luogo con risposta

Figura 1. Attività Dettagli del luogo con la risposta visualizzata.

9. Aggiungere Place Autocomplete

Creare una schermata di completamento automatico

Un layout activity_autocomplete.xml con un LinearLayout vuoto è disponibile nella cartella app/src/main/res/layout/. Compila il layout lineare aggiungendo questo codice tra le parentesi <LinearLayout>.

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/autocomplete_fragment"
        android:background="@android:color/white"
        android:name="com.google.android.libraries.places.widget.AutocompleteSupportFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/autocomplete_response_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:textIsSelectable="true" />

Questo codice aggiunge un widget AutocompleteSupportFragment e una TextView per visualizzare le informazioni della risposta. Le stringhe associate sono definite nel file src/main/res/values/strings.xml.

Creare un'attività di completamento automatico

  1. Crea un file AutocompleteActivity.kt nella cartella src/main/java/com/google/codelabs/maps/placesdemo/ e definiscilo con questo codice:
@ExperimentalCoroutinesApi
class AutocompleteActivity : AppCompatActivity() {
    private lateinit var responseView: TextView

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

        // Set up view objects
        responseView = findViewById(R.id.autocomplete_response_content)
        val autocompleteFragment =
            supportFragmentManager.findFragmentById(R.id.autocomplete_fragment)
                    as AutocompleteSupportFragment
    }
}

Questo codice associa l'attività alle visualizzazioni e a AutocompleteSupportFramgent che hai definito nel file di layout.

  1. Successivamente, definisci cosa succede quando l'utente seleziona una delle previsioni presentate da Place Autocomplete. Aggiungi questo codice alla fine della funzione onCreate:
        val placeFields: List<Place.Field> =
            listOf(Place.Field.NAME, Place.Field.ID, Place.Field.ADDRESS, Place.Field.LAT_LNG)
        autocompleteFragment.setPlaceFields(placeFields)

        // Listen to place selection events
        lifecycleScope.launchWhenCreated {
            autocompleteFragment.placeSelectionEvents().collect { event ->
                when (event) {
                    is PlaceSelectionSuccess -> {
                        val place = event.place
                        responseView.text = prettyPrintAutocompleteWidget(place, false)
                    }

                    is PlaceSelectionError -> Toast.makeText(
                        this@AutocompleteActivity,
                        "Failed to get place '${event.status.statusMessage}'",
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
        }

Questo codice definisce i campi da richiedere per il luogo, rileva un evento di selezione del luogo e rileva se l'operazione è riuscita o meno. Se la richiesta va a buon fine, la funzione compila il TextView con i dettagli del luogo. Tieni presente che Place Autocomplete restituisce un oggetto Place; non è necessario effettuare una richiesta Places Details separata quando utilizzi il widget Place Autocomplete.

Aggiungere l'attività di completamento automatico al manifest

Aggiungi un elemento <activity> per AutocompleteActivity come elemento secondario dell'elemento <application> nel file AndroidManifest.xml, che si trova in app/src/main:

        <activity android:name=".AutocompleteActivity" android:label="@string/autocomplete_fragment_demo_title" />

Aggiungi l'attività di completamento automatico al menu demo

Come prima, aggiungi la demo di completamento automatico del luogo alla schermata Home aggiungendola all'elenco nel modulo Demo. Ora che hai creato un'attività di completamento automatico dei luoghi, aggiungila al file Demo.kt nella cartella src/main/java/com/google/codelabs/maps/placesdemo/. Incolla questo codice immediatamente dopo l'elemento DETAILS_FRAGMENT_DEMO:

    AUTOCOMPLETE_FRAGMENT_DEMO(
        R.string.autocomplete_fragment_demo_title,
        R.string.autocomplete_fragment_demo_description,
        AutocompleteActivity::class.java
    ),

Le stringhe associate sono definite nel file src/main/res/values/strings.xml.

Esegui l'app

  1. Esegui l'app. Questa volta dovresti vedere due elementi nell'elenco della schermata Home.
  2. Tocca la riga di Place Autocomplete. Dovresti visualizzare un popup di input di completamento automatico dei luoghi come mostrato nella Figura 2.
  3. Inizia a digitare il nome di un luogo. Può essere il nome di un'attività, un indirizzo o una regione geografica. I suggerimenti devono essere visualizzati durante la digitazione.
  4. Seleziona una delle previsioni. Le previsioni dovrebbero scomparire e la TextView dovrebbe ora mostrare i dettagli sul luogo selezionato, come mostrato nella Figura 3.

Attività di completamento automatico dopo che l&#39;utente tocca il campo di input

Figura 2. Attività di completamento automatico dopo che l'utente tocca il campo di input.

Completamento automatico dell&#39;attività dopo che l&#39;utente ha digitato e selezionato &quot;Cascate del Niagara&quot;

Figura 3. Attività di completamento automatico che mostra i dettagli del luogo dopo che l'utente ha digitato e selezionato "Cascate del Niagara".

10. Ottenere il luogo attuale del dispositivo

Creare una schermata Luogo attuale

Nella cartella app/src/main/res/layout/ è stato fornito un layout activity_current.xml con un LinearLayout vuoto. Compila il layout lineare aggiungendo il seguente codice tra le parentesi <LinearLayout>.

    <Button
        android:id="@+id/current_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/current_button" />

    <TextView
        android:id="@+id/current_response_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:scrollbars = "vertical"
        android:textIsSelectable="true" />

Creare un'attività Luogo attuale

  1. Crea un file CurrentPlaceActivity.kt nella cartella src/main/java/com/google/codelabs/maps/placesdemo/ e definiscilo con questo codice:
@ExperimentalCoroutinesApi
class CurrentPlaceActivity : AppCompatActivity(), OnMapReadyCallback {
    private lateinit var placesClient: PlacesClient
    private lateinit var currentButton: Button
    private lateinit var responseView: TextView

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

        // Retrieve a PlacesClient (previously initialized - see DemoApplication)
        placesClient = Places.createClient(this)

        // Set view objects
        currentButton = findViewById(R.id.current_button)
        responseView = findViewById(R.id.current_response_content)

        // Set listener for initiating Current Place
        currentButton.setOnClickListener {
            checkPermissionThenFindCurrentPlace()
        }
    }
}

Questo codice associa l'attività alle visualizzazioni definite nel file di layout. Inoltre, aggiunge un listener di clic al pulsante per chiamare la funzione checkPermissionThenFindCurrentPlace quando viene fatto clic sul pulsante.

  1. Definisci checkPermissionThenFindCurrentPlace() per verificare l'autorizzazione di accesso alla posizione esatta e richiedere l'autorizzazione se non è ancora stata concessa. Incolla questo codice dopo la funzione onCreate.
    /**
     * Checks that the user has granted permission for fine or coarse location.
     * If granted, finds current Place.
     * If not yet granted, launches the permission request.
     * See https://developer.android.com/training/permissions/requesting
     */
    private fun checkPermissionThenFindCurrentPlace() {
        when {
            (ContextCompat.checkSelfPermission(
                this,
                ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(
                this,
                ACCESS_COARSE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED) -> {
                // You can use the API that requires the permission.
                findCurrentPlace()
            }

            shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)
            -> {
                Log.d(TAG, "Showing permission rationale dialog")
                // TODO: In an educational UI, explain to the user why your app requires this
                // permission for a specific feature to behave as expected. In this UI,
                // include a "cancel" or "no thanks" button that allows the user to
                // continue using your app without granting the permission.
            }

            else -> {
                // Ask for both the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions.
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(
                        ACCESS_FINE_LOCATION,
                        ACCESS_COARSE_LOCATION
                    ),
                    PERMISSION_REQUEST_CODE
                )
            }
        }
    }

    companion object {
        private const val TAG = "CurrentPlaceActivity"
        private const val PERMISSION_REQUEST_CODE = 9
    }
  1. Quando il ramo else della funzione checkPermissionThenFindCurrentPlace chiama requestPermissions, l'app mostra all'utente una finestra di dialogo di richiesta di autorizzazione. Se l'utente utilizza un dispositivo con un sistema operativo precedente ad Android 12, può concedere solo l'autorizzazione di accesso alla posizione esatta (precisa). Se l'utente utilizza un dispositivo con Android 12 o versioni successive, avrà la possibilità di fornire una posizione approssimativa (generica) anziché una posizione esatta (precisa), come mostrato nella Figura 4.

Richiedere l&#39;autorizzazione utente su un dispositivo con Android 12 o versioni successive

Immagine 4. Quando viene richiesta l'autorizzazione utente su un dispositivo con Android 12 o versioni successive, viene presentata l'opzione di concedere la posizione esatta o approssimativa.

Dopo che l'utente ha risposto alla finestra di dialogo delle autorizzazioni di sistema, il sistema richiama l'implementazione di onRequestPermissionsResult della tua app. Il sistema passa la risposta dell'utente alla finestra di dialogo delle autorizzazioni, nonché il codice di richiesta che hai definito. Esegui l'override di onRequestPermissionResult per gestire il codice di richiesta per le autorizzazioni di localizzazione relative a questa attività Current Place incollando il seguente codice sotto checkPermissionThenFindCurrentPlace.

    @SuppressLint("MissingPermission")
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>, grantResults: IntArray
    ) {
        if (requestCode != PERMISSION_REQUEST_CODE) {
            super.onRequestPermissionsResult(
                requestCode,
                permissions,
                grantResults
            )
            return
        } else if (
            permissions.toList().zip(grantResults.toList())
                .firstOrNull { (permission, grantResult) ->
                    grantResult == PackageManager.PERMISSION_GRANTED && (permission == ACCESS_FINE_LOCATION || permission == ACCESS_COARSE_LOCATION)
                } != null
        )
        // At least one location permission has been granted, so proceed with Find Current Place
        findCurrentPlace()
    }
  1. Una volta concessa l'autorizzazione, la funzione findCurrentPlace verrà eseguita. Definisci la funzione con questo codice dopo la funzione onRequestPermissionsResult.
    /**
     * Fetches a list of [PlaceLikelihood] instances that represent the Places the user is
     * most likely to be at currently.
     */
    @RequiresPermission(anyOf = [ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION])
    private fun findCurrentPlace() {
        // Use fields to define the data types to return.
        val placeFields: List<Place.Field> =
            listOf(Place.Field.NAME, Place.Field.ID, Place.Field.ADDRESS, Place.Field.LAT_LNG)

        // Call findCurrentPlace and handle the response (first check that the user has granted permission).
        if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) ==
            PackageManager.PERMISSION_GRANTED ||
            ContextCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION) ==
            PackageManager.PERMISSION_GRANTED
        ) {
            // Retrieve likely places based on the device's current location
            currentButton.isEnabled = false
            lifecycleScope.launch {
                val response = placesClient.awaitFindCurrentPlace(placeFields)

                responseView.text = response.prettyPrint()

                // Enable scrolling on the long list of likely places
                val movementMethod = ScrollingMovementMethod()
                responseView.movementMethod = movementMethod
            }
        } else {
            Log.d(TAG, "LOCATION permission not granted")
            checkPermissionThenFindCurrentPlace()
        }
    }

Questo codice definisce i campi da richiedere per i luoghi probabili, crea un FindCurrentPlaceRequest, avvia l'attività e compila il TextView con i dettagli richiesti.

Aggiungi l'attività Luogo attuale al manifest

Aggiungi un elemento <activity> per CurrentPlaceActivity come elemento secondario dell'elemento <application> nel file AndroidManifest.xml, che si trova in app/src/main:

        <activity android:name=".CurrentPlaceActivity" android:label="@string/current_demo_title" />

Aggiungi l'attività Luogo attuale al menu demo

Come prima, aggiungi la demo di Current Place alla schermata Home aggiungendola all'elenco nel modulo Demo. Ora che hai creato un'attività Luogo attuale, aggiungila al file Demo.kt nella cartella src/main/java/com/google/codelabs/maps/placesdemo/. Incolla questo codice immediatamente dopo l'elemento AUTOCOMPLETE_FRAGMENT_DEMO:

    CURRENT_FRAGMENT_DEMO(
        R.string.current_demo_title,
        R.string.current_demo_description,
        CurrentPlaceActivity::class.java
    ),

Le stringhe associate sono definite nel file src/main/res/values/strings.xml.

Esegui l'app

  1. Esegui l'app. Questa volta dovresti vedere tre elementi nell'elenco della schermata Home.
  2. Tocca la riga Luogo attuale. Dovresti vedere un pulsante sullo schermo.
  3. Tocca il pulsante. Se non hai mai concesso l'autorizzazione di accesso alla posizione a questa app, dovrebbe essere visualizzata una richiesta di autorizzazione.
  4. Concedi all'app l'autorizzazione ad accedere alla posizione del dispositivo.
  5. Tocca di nuovo il pulsante. Questa volta dovrebbe essere visualizzato un elenco di un massimo di 20 luoghi nelle vicinanze e le relative probabilità, come mostrato nella Figura 5.

Mostra le corrispondenze più probabili per il luogo attuale in base alla posizione segnalata del dispositivo

Figura 5. Mostra le probabili corrispondenze di Luogo attuale per la posizione segnalata del dispositivo.

11. Visualizzare il luogo attuale su una mappa

Aggiungi la dipendenza Map

Nel file build.gradle a livello di modulo, aggiungi la dipendenza da Google Play Services per l'SDK di Maps per Android.

app/build.gradle

dependencies {
    // ...
    implementation 'com.google.android.gms:play-services-maps:18.2.0'
}

Aggiorna il file manifest di Android per tenere conto delle mappe

Aggiungi i seguenti elementi meta-data all'interno dell'elemento application.

Questi incorporano la versione di Google Play Services con cui è stata compilata l'app e specificano la chiave API.

AndroidManifest.xml

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${MAPS_API_KEY}" />

Aggiungi la chiave API a secrets.properties

Apri il file secrets.properties nella directory di primo livello, quindi aggiungi il codice seguente. Sostituisci YOUR_API_KEY con la tua chiave API.

MAPS_API_KEY=YOUR_API_KEY

Apri il file local.defaults.properties nella directory di primo livello, la stessa cartella del file secrets.properties, quindi aggiungi il seguente codice.

MAPS_API_KEY=DEFAULT_API_KEY

Controlla la chiave API

In onCreate(), l'app controllerà la chiave API di Maps e inizializzerà il frammento di supporto di Maps. getMapAsync() viene utilizzato per registrarsi al richiamo della mappa.

Aggiungi il seguente codice per farlo.

CurrentPlaceActivity.kt

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

        val apiKey = BuildConfig.MAPS_API_KEY

        // Log an error if apiKey is not set.
        if (apiKey.isEmpty() || apiKey == "DEFAULT_API_KEY") {
            Log.e("Places test", "No api key")
            finish()
            return
        }

        // Retrieve a PlacesClient (previously initialized - see DemoApplication)
        placesClient = Places.createClient(this)
        (supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment?)?.getMapAsync(this)

        // ...
    }

Crea il layout della mappa

  1. Nella cartella app/src/main/res/layout/, crea il file di layout fragment_map.xml e inserisci il seguente codice.

res/layout/fragment_map.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.google.codelabs.maps.placesdemo.CurrentPlaceActivity" />

Definisce un SupportMapFragment che funge da contenitore per la mappa e fornisce l'accesso all'oggetto GoogleMap.

  1. Nel layout activity_current.xml disponibile nella cartella app/src/main/res/layout/, aggiungi il seguente codice in fondo al layout lineare.

res/layout/activity_current.xml

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:paddingBottom="16dp"
        android:text="@string/showing_most_likely_place"
        style="@style/TextAppearance.AppCompat.Title"/>

    <include layout="@layout/fragment_map"/>

Il valore TextView aggiunto fa riferimento a una nuova risorsa stringa che deve essere creata.

  1. In app/src/main/res/values/strings.xml, aggiungi la seguente risorsa stringa.

res/values/strings.xml

<string name="showing_most_likely_place">Showing most likely place</string>

Poiché sono state aggiunte visualizzazioni aggiuntive per la mappa, l'TextView che mostra l'elenco dei luoghi deve avere un'altezza impostata in modo che queste visualizzazioni rimangano visibili.

  1. Aggiungi l'attributo maxHeight a TextView con ID current_response_content

res/layout/activity_current.xml

android:maxHeight="200dp"

Implementa OnMapReadyCallback

Implementa l'interfaccia OnMapReadyCallback aggiungendola alla dichiarazione della classe e sostituisci il metodo onMapReady() per configurare la mappa quando l'oggetto GoogleMap è disponibile:

CurrentPlaceActivity.kt

class CurrentPlaceActivity : AppCompatActivity(), OnMapReadyCallback {

Alla fine della classe, aggiungi il seguente codice:

CurrentPlaceActivity.kt

    override fun onMapReady(map: GoogleMap) {
        this.map = map
        lastKnownLocation?.let { location ->
            map.moveCamera(
                CameraUpdateFactory.newLatLngZoom(
                    location,
                    DEFAULT_ZOOM
                )
            )
        }
    }

Il callback richiede alcune variabili di classe per funzionare correttamente. Subito dopo l'intestazione della classe, aggiungi quanto segue:

CurrentPlaceActivity.kt

private var map: GoogleMap? = null
private var lastKnownLocation: LatLng? = null

Aggiungi il seguente codice all'oggetto companion della classe:

CurrentPlaceActivity.kt

private const val DEFAULT_ZOOM = 15f

Esegui l'app

  1. Esegui l'app.
  2. Tocca la riga Luogo attuale. Dovresti vedere un pulsante sullo schermo.
  3. Tocca il pulsante. Se non hai mai concesso l'autorizzazione di accesso alla posizione a questa app, dovrebbe essere visualizzata una richiesta di autorizzazione.
  4. Concedi all'app l'autorizzazione ad accedere alla posizione del dispositivo.
  5. Tocca di nuovo il pulsante. Verrà visualizzata la mappa.

Attività di Luogo attuale che mostra la mappa

Immagine 6. Attività Luogo attuale che mostra la mappa.

Aggiornare la mappa con un luogo

Alla fine della classe, aggiungi il seguente codice:

CurrentPlaceActivity.kt

private data class LikelyPlace(
    val name: String,
    val address: String,
    val attribution: List<String>,
    val latLng: LatLng
)

private fun PlaceLikelihood.toLikelyPlace(): LikelyPlace? {
    val name = this.place.name
    val address = this.place.address
    val latLng = this.place.latLng
    val attributions = this.place.attributions ?: emptyList()

    return if (name != null && address != null && latLng != null) {
        LikelyPlace(name, address, attributions, latLng)
    } else {
        null
    }
}

Questi vengono utilizzati per archiviare e formattare i dati del luogo.

All'inizio della classe, aggiungi il seguente codice per creare una variabile utilizzata per memorizzare i dati del luogo restituito.

CurrentPlaceActivity.kt

private val likelyPlaces = mutableListOf<LikelyPlace>()

In questo passaggio , il codice verrà modificato in modo che all'utente venga mostrato un elenco di luoghi e ne scelga uno da visualizzare sulla mappa. Tutti i dati dei luoghi vengono visualizzati in un elenco sullo schermo.

Nella funzione findCurrentPlace, nel blocco lifecycleScope.launch prima di questa riga di codice

CurrentPlaceActivity.kt

responseView.text = response.prettyPrint()

aggiungi il seguente codice:

CurrentPlaceActivity.kt

                likelyPlaces.clear()

                likelyPlaces.addAll(
                    response.placeLikelihoods.take(M_MAX_ENTRIES).mapNotNull { placeLikelihood ->
                        placeLikelihood.toLikelyPlace()
                    }
                )

                openPlacesDialog()

Questo codice richiede una costante per il numero massimo di luoghi da mostrare.

Nell'oggetto complementare, aggiungi il codice per la costante.

CurrentPlaceActivity.kt

private const val M_MAX_ENTRIES = 5

Aggiungi il seguente codice che crea la finestra di dialogo che consente all'utente di selezionare un luogo.

CurrentPlaceActivity.kt

    /**
     * Displays a form allowing the user to select a place from a list of likely places.
     */
    private fun openPlacesDialog() {
        // Ask the user to choose the place where they are now.
        val listener =
            DialogInterface.OnClickListener { _, which -> // The "which" argument contains the position of the selected item.
                val likelyPlace = likelyPlaces[which]
                lastKnownLocation = likelyPlace.latLng

                val snippet = buildString {
                    append(likelyPlace.address)
                    if (likelyPlace.attribution.isNotEmpty()) {
                        append("\n")
                        append(likelyPlace.attribution.joinToString(", "))
                    }
                }

                val place = Place.builder().apply {
                    name = likelyPlace.name
                    latLng = likelyPlace.latLng
                }.build()

                map?.clear()

                setPlaceOnMap(place, snippet)
            }

        // Display the dialog.
        AlertDialog.Builder(this)
            .setTitle(R.string.pick_place)
            .setItems(likelyPlaces.map { it.name }.toTypedArray(), listener)
            .setOnDismissListener {
                currentButton.isEnabled = true
            }
            .show()
    }

Seguendo le best practice di Android, la finestra di dialogo fa riferimento a una risorsa stringa che deve essere aggiunta al file di risorse strings.xml che si trova nella cartella app/src/main/res/values/.

Aggiungi il seguente codice a strings.xml:

res/values/strings.xml

    <string name="pick_place">Choose a place</string>

Queste funzioni chiamano quindi la funzione setPlaceOnMap, che sposta la videocamera e posiziona un indicatore nella posizione selezionata.

Aggiungi il codice seguente:

CurrentPlaceActivity.kt

    private fun setPlaceOnMap(place: Place?, markerSnippet: String?) {
        val latLng = place?.latLng ?: defaultLocation
        map?.moveCamera(
            CameraUpdateFactory.newLatLngZoom(
                latLng,
                DEFAULT_ZOOM
            )
        )
        map?.addMarker(
            MarkerOptions()
                .position(latLng)
                .title(place?.name)
                .snippet(markerSnippet)
        )
    }

È inoltre consigliabile salvare e ripristinare lo stato delle mappe.

Per salvare il suo stato, esegui l'override della funzione onSaveInstanceState e aggiungi il seguente codice:

CurrentPlaceActivity.kt

    /**
     * Saves the state of the map when the activity is paused.
     */
    override fun onSaveInstanceState(outState: Bundle) {
        outState.putParcelable(KEY_LOCATION, lastKnownLocation)
        super.onSaveInstanceState(outState)
    }

Per ripristinarne lo stato, in onCreate aggiungi il seguente codice dopo la chiamata a setContentView:

CurrentPlaceActivity.kt

        if (savedInstanceState != null) {
            lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
        }

Il salvataggio e il ripristino richiedono una chiave, che è una costante dell'oggetto complementare.

Nel blocco dell'oggetto companion, aggiungi quanto segue:

CurrentPlaceActivity.kt

        // Key for storing activity state.
        private const val KEY_LOCATION = "location"

Esegui l'app

  1. Esegui l'app.
  2. Tocca la riga Luogo attuale. Dovresti vedere un pulsante sullo schermo.
  3. Tocca il pulsante. Se non hai mai concesso l'autorizzazione di accesso alla posizione a questa app, dovrebbe essere visualizzata una richiesta di autorizzazione.
  4. Concedi all'app l'autorizzazione ad accedere alla posizione del dispositivo.
  5. Tocca di nuovo il pulsante.
  6. Scegli un luogo toccandolo. La mappa verrà ingrandita e centrata con un indicatore posizionato nella località selezionata.

Mappa con indicatore nella posizione selezionata

Immagine 7. Mappa con indicatore nella posizione selezionata.

12. Complimenti

Hai creato correttamente un'app per Android con Places SDK for Android.

Che cosa hai imparato

Passaggi successivi

  • Esplora o crea un fork del repository GitHub android-places-demos di esempi e demo per trovare altra ispirazione.
  • Scopri di più con altri codelab Kotlin per creare app per Android con Google Maps Platform.
  • Aiutaci a creare i contenuti che ritieni più utili rispondendo alla seguente domanda:

Quali altri codelab vorresti vedere?

Visualizzazione dei dati sulle mappe Scopri di più sulla personalizzazione dello stile delle mie mappe Creare edifici per interazioni 3D nelle mappe

Il codelab che ti interessa non è presente nell'elenco? Richiedilo con un nuovo problema qui.