Criar um seletor do seu lugar atual no Android (Java)

1. Antes de começar

Aprenda a usar a Plataforma Google Maps e o SDK do Places para Android para apresentar uma lista de lugares aos usuários ao identificar a localização atual deles.

bd07a9ad2cb27a06.png

Pré-requisitos

  • Habilidades básicas em Java

Atividades deste codelab

  • Adicionar um mapa a um app Android
  • Usar permissões de localização para geolocalizar o usuário
  • Buscar lugares perto da localização atual do usuário
  • Apresentar os possíveis lugares para o usuário identificar a localização atual dele

O que você criará

Você cria seu app Android do zero, mas pode fazer o download do exemplo de código para comparação ao depurar. Faça o download do exemplo de código do GitHub ou, se você tiver configurado o Git para usar a linha de comando, digite o seguinte:

git clone https://github.com/googlecodelabs/current-place-picker-android.git

Se você encontrar problemas (bugs no código, erros gramaticais, enunciados confusos, entre outros) enquanto trabalha neste codelab, informe o problema pelo link Informar um erro no canto inferior esquerdo da tela.

2. Começar

Antes de iniciar este codelab, você precisa configurar o seguinte:

Android Studio

Faça o download do Android Studio em https://developer.android.com/studio.

Se você já tem o Android Studio, verifique se tem a versão mais recente. Para isso, clique em Android Studio > Check for Updates....

1f36bae83b64e33.png

Este laboratório foi escrito usando o Android Studio 3.4.

SDK do Android

No Android Studio, você pode configurar os SDKs desejados usando o SDK Manager. Este laboratório usa o SDK do Android Q.

  1. Na tela de boas-vindas do Android Studio, clique em Configure > SDK Manager.

d3fa03c269ec231c.png

  1. Marque a caixa de seleção do SDK e clique em Apply.

Se você ainda não tiver o SDK, o download dele começará na sua máquina.

884e0aa1314f70d.png

Google Play Services

No SDK Manager, você também precisará instalar o Google Play Services.

  1. Clique na guia SDK Tools e marque a caixa de seleção Google Play Services.

Atualize se o status for Update available.

ad6211fd78f3b629.png

3. Preparar o emulador

Para executar o app, conecte seu próprio dispositivo ou use o Android Emulator.

Se você estiver usando seu dispositivo, pule para Instruções para dispositivo real: atualizar o Google Play Services no fim desta página.

Adicionar um emulador

  1. Na tela de boas-vindas do Android Studio, clique em Configure > AVD Manager.

5dd2d14c9c56d3f9.png

Isso abre a caixa de diálogo Android Virtual Device Manager.

  1. Clique em Create Virtual Device... para abrir uma lista dos dispositivos que podem ser escolhidos.

2d44eada384f8b35.png

  1. Escolha um dispositivo com o ícone do Google Play d5722488d80cd6be.png na coluna Play Store e, depois, clique em Next.

e0248f1c6e85ab7c.png

Você verá um conjunto das imagens do sistema que podem ser instaladas. Se a segmentação Q do Android 9.+ (Google Play) tiver a palavra Download ao lado, clique em Download.

316d0d1efabd9f24.png

  1. Clique em Next para nomear o dispositivo virtual e, depois, em Finish.

Você retornará à lista Your Virtual Devices.

  1. Clique em Iniciar ba8adffe56d3b678.png ao lado do novo dispositivo:

7605864ed27f77ea.png

Depois de alguns instantes, o emulador será aberto.

Instruções do emulador: atualizar o Google Play Services

  1. Quando o emulador for iniciado, clique em ... na barra de navegação exibida**.**

2e1156e02643d018.png

Isso abre a caixa de diálogo Extended controls.

  1. Clique em Google Play no menu.

Se houver uma atualização disponível, clique em Update.

5afd2686c5cad0e5.png

  1. Faça login no emulador com uma Conta do Google.

Você pode usar sua conta ou criar uma nova gratuitamente para manter os testes separados das suas informações pessoais.

O Google Play Services será aberto.

  1. Clique em Atualizar para ter a versão mais recente do Google Play Services.

f4bc067e80630b9c.png

Se for necessário concluir a configuração da conta e adicionar uma opção de pagamento, clique em Pular.

Definir a localização no emulador

  1. Quando o emulador for iniciado, digite "maps" na barra de pesquisa da tela inicial para exibir o ícone do app Google Maps.

2d996aadd53685a6.png

  1. Clique no ícone para iniciar.

Você verá um mapa padrão.

  1. No canto inferior direito do mapa, clique em Seu local c5b4e2fda57a7e71.png.

É preciso permitir que o smartphone use a localização.

f2b68044eabca151.png

  1. Clique em ... para abrir o menu Extended Controls.
  2. Clique na guia Location.
  3. Insira a latitude e a longitude.

Você pode inserir qualquer informação aqui, mas escolha uma área com muitos lugares.

Para replicar os resultados deste codelab, use a latitude 20.7818 e a longitude -156.4624, equivalente à cidade de Kihei, em Maui, Havaí.

  1. Clique em Send, e o mapa será atualizado com esse local.

f9576b35218f4187.png

Você já pode executar o app e testá-lo com a localização.

Instruções para dispositivo real: atualizar o Google Play Services

Se você estiver usando um dispositivo Android, siga estas etapas:

  1. Use a barra de pesquisa na tela inicial para procurar e abrir o Google Play Services.
  2. Clique em Mais detalhes.

Se disponível, clique em Atualizar.

ad16cdb975b5c3f7.png baf0379ef8a9c88c.png

4. Criar o shell do app com uma atividade no Google Maps

  1. Na tela de boas-vindas do Android Studio, selecione Start a new Android Studio project.
  2. Na guia Phone and Tablet, selecione Google Maps Activity.

c9c80aa8211a8761.png

A caixa de diálogo Configure your project é aberta. Aqui você vai nomear o app e criar o pacote com base no seu domínio.

Aqui estão as configurações de um app chamado Current Place, que corresponde ao pacote com.google.codelab.currentplace.

37f5b93b94ee118c.png

  1. Escolha Java como a linguagem e selecione Use androidx. artifacts*.

Mantenha os valores padrão das outras configurações.

  1. Clique em Concluir.

5. Adicionar dependências dos Serviços do Google ao arquivo de compilação do Gradle

Para acessar as permissões de localização no Android, você precisa da API Google Location and Activity Recognition do Google Play Services. Para mais informações sobre como adicionar essa e outras APIs do Google Play Services, consulte Configurar o Google Play Services.

Os projetos do Android Studio normalmente têm dois arquivos build.gradle. Um é para o projeto geral e o outro é para o app. Se você tiver o explorador de projetos do Android Studio na visualização do Android, verá ambos na pasta Gradle Scripts. Para adicionar serviços do Google, será necessário editar o arquivo build.gradle (Module: app).

f3043429cf719c47.png

  1. Adicione duas linhas à seção dependencies para adicionar Serviços do Google relacionados a localização e a API Places (exemplo de código no contexto).

build.gradle (módulo: app)

plugins {
  id 'com.android.application'
}

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.google.codelab.currentplace"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'com.google.android.gms:play-services-maps:16.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

    implementation 'com.google.android.gms:play-services-location:16.0.0'
    implementation 'com.google.android.libraries.places:places:1.1.0'
}

6. Ativar as APIs da Plataforma Google Maps e gerar uma chave de API

Para a etapa de ativação a seguir, é necessário ativar o SDK do Maps para Android e a API Places.

Configurar a Plataforma Google Maps

Caso você ainda não tenha uma conta do Google Cloud Platform e um projeto com faturamento ativado, veja como criá-los no guia Primeiros passos com a Plataforma Google Maps.

  1. No Console do Cloud, clique no menu suspenso do projeto e selecione o projeto que você quer usar neste codelab.

  1. Ative as APIs e os SDKs da Plataforma Google Maps necessários para este codelab no Google Cloud Marketplace. Para fazer isso, siga as etapas descritas neste vídeo ou nesta documentação.
  2. Gere uma chave de API na página Credenciais do Console do Cloud. Siga as etapas indicadas neste vídeo ou nesta documentação. Todas as solicitações feitas à Plataforma Google Maps exigem uma chave de API.

Copie a chave de API que você acabou de criar. Volte para o Android Studio e localize o arquivo google_maps_api.xml em Android > app > res > values.

Substitua YOUR_KEY_HERE pela chave de API que você copiou.

aa576e551a7a1009.png

Seu app está configurado.

7. Editar o arquivo de layout

  1. No explorador de projetos, abra o arquivo activity_maps.xml em Android > app > res > layout.

4e0d986480c57efa.png

  1. Você verá a IU básica aberta à direita da tela, com guias na parte inferior onde é possível selecionar o editor de texto ou design para seu layout. Selecione Text e substitua todo o conteúdo do arquivo de layout pelo seguinte:

activity_maps.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout 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:fitsSystemWindows="true"
    android:orientation="vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:minHeight="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:titleTextColor="@android:color/white"
        android:background="@color/colorPrimary" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <fragment
            android:id="@+id/map"
            android:name="com.google.android.gms.maps.SupportMapFragment"
            android:layout_width="match_parent"
            android:layout_height="349dp"
            tools:context=".MapsActivity" />

        <ListView
            android:id="@+id/listPlaces"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>

</LinearLayout>

Você verá uma interface do usuário parecida com esta:

1bf786808a4697ce.png

8. Configurar a barra de apps

Para disponibilizar ao usuário um botão em que ele possa clicar quando quiser escolher o lugar atual, adicione uma barra de apps com um ícone que encontre o lugar atual do usuário e exiba locais próximos. Ela terá esta aparência.

3a17c92b613a26c5.png

Em um smartphone, apenas o ícone é exibido. Em tablets, onde há mais espaço, o texto também é incluído.

Criar o ícone

  1. No explorador de projetos, clique em Android > app, depois clique com o botão direito na pasta res e selecione New > Image Asset.

O Asset Studio será aberto.

  1. No menu Icon Type, clique em Action Bar and Tab Icons.
  2. Dê um nome para seu recurso ic_geolocate.
  3. Selecione Clip Art como o tipo de recurso**.**
  4. Clique na imagem ao lado de Clip Art.

A janela Select Icon será aberta.

  1. Escolha um ícone.

Use a barra de pesquisa para encontrar ícones relacionados à sua intent.

  1. Pesquise por location e escolha um ícone relacionado ao local.

O ícone my location é igual ao usado no app Google Maps quando o usuário quer posicionar a câmera na localização atual.

  1. Clique em OK > Next > Finish, e verifique se há uma pasta nova chamada drawable que contém os novos arquivos de ícone.

b9e0196137ed18ae.png

Adicionar recursos de string

  1. No explorador de projetos, clique em Android > app > res > values e abra o arquivo strings.xml.
  2. Adicione as seguintes linhas após <string name="title_activity_maps">Map</string>:

strings.xml

    <string name="action_geolocate">Pick Place</string>
    <string name="default_info_title">Default Location</string>
    <string name="default_info_snippet">No places found, because location permission is disabled.</string>

A primeira linha é usada na barra de apps quando há espaço para incluir um rótulo de texto ao lado do ícone. As outras são usadas para os marcadores que você adicionar ao mapa.

Agora o código no arquivo tem esta aparência:

<resources>
    <string name="app_name">Current Place</string>
    <string name="title_activity_maps">Map</string>
    <string name="action_geolocate">Pick Place</string>
    <string name="default_info_title">Default Location</string>
    <string name="default_info_snippet">No places found, because location permission is disabled.</string>
</resources>

Adicionar a barra de apps

  1. No explorador de projetos, clique em Android > app, depois clique com o botão direito na pasta res e selecione New > Directory para criar um novo subdiretório em app/src/main/res.
  2. Nomeie o diretório como menu.
  3. Clique com o botão direito do mouse na pasta menu e selecione New > File.
  4. Nomeie o arquivo como menu.xml.
  5. Cole este código:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- "Locate me", should appear as action button if possible -->
    <item
        android:id="@+id/action_geolocate"
        android:icon="@drawable/ic_geolocate"
        android:title="@string/action_geolocate"
        app:showAsAction="always|withText" />

</menu>

Atualizar o estilo da barra de apps

  1. No explorador de projetos, navegue até Android > app > res > values e abra o arquivo styles.xml.
  2. Na tag <style>, edite a propriedade pai como "Theme.AppCompat.NoActionBar".
  3. Observe a propriedade name, que será usada na próxima etapa.

styles.xml

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">

Atualizar o tema do app em AndroidManifest.xml

  1. Clique em Android > app > manifests e abra o arquivo AndroidManifest.xml.
  2. Encontre a linha android:theme e edite ou confirme o valor como @style/AppTheme.

AndroidManifest.xml

   <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

Agora você já pode começar a programar.

9. Inicializar o app

  1. No explorador de projetos, localize o arquivo MapsActivity.java.

Ele está na pasta correspondente ao pacote criado para seu app na etapa 1.

8b0fa27d417f5f55.png

  1. Abra o arquivo e você acessará o editor de código Java.

Importar o SDK do Places e outras dependências

Adicione essas linhas na parte superior de MapsActivity.java, substituindo as instruções de importação existentes.

Elas incluem as importações existentes e adicionam muitas outras usadas no código neste codelab.

MapsActivity.java

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.libraries.places.api.Places;
import com.google.android.libraries.places.api.model.Place;
import com.google.android.libraries.places.api.model.PlaceLikelihood;
import com.google.android.libraries.places.api.net.FindCurrentPlaceRequest;
import com.google.android.libraries.places.api.net.FindCurrentPlaceResponse;
import com.google.android.libraries.places.api.net.PlacesClient;

import java.util.Arrays;
import java.util.List;

Atualizar a assinatura da classe

A API Places usa componentes do AndroidX para oferecer compatibilidade com versões anteriores. Portanto, você precisa defini-la para ampliar o AppCompatActivity. Ela substitui a extensão FragmentActivity que é definida por padrão para uma atividade no Google Maps.

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback {

Adicionar variáveis de classe

Em seguida, declare as diversas variáveis de classe usadas em diferentes métodos de classe. Elas incluem os elementos da IU e os códigos de status e estão logo abaixo da declaração de variável para GoogleMap mMap.

    // New variables for Current Place picker
    private static final String TAG = "MapsActivity";
    ListView lstPlaces;
    private PlacesClient mPlacesClient;
    private FusedLocationProviderClient mFusedLocationProviderClient;

    // The geographical location where the device is currently located. That is, the last-known
    // location retrieved by the Fused Location Provider.
    private Location mLastKnownLocation;

    // A default location (Sydney, Australia) and default zoom to use when location permission is
    // not granted.
    private final LatLng mDefaultLocation = new LatLng(-33.8523341, 151.2106085);
    private static final int DEFAULT_ZOOM = 15;
    private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1;
    private boolean mLocationPermissionGranted;

    // Used for selecting the Current Place.
    private static final int M_MAX_ENTRIES = 5;
    private String[] mLikelyPlaceNames;
    private String[] mLikelyPlaceAddresses;
    private String[] mLikelyPlaceAttributions;
    private LatLng[] mLikelyPlaceLatLngs;

Atualizar o método onCreate

Você precisará atualizar o método onCreate para gerenciar permissões de usuário do tempo de execução para Serviços de localização, configurar os elementos da IU e criar o cliente da API Places.

Adicione as seguintes linhas de código referentes à barra de ferramentas de ação, à configuração de visualizações e ao cliente do Places ao fim do método onCreate() existente.

MapsActivity.java onCreate()

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        //
        // PASTE THE LINES BELOW THIS COMMENT
        //

        // Set up the action toolbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Set up the views
        lstPlaces = (ListView) findViewById(R.id.listPlaces);

        // Initialize the Places client
        String apiKey = getString(R.string.google_maps_key);
        Places.initialize(getApplicationContext(), apiKey);
        mPlacesClient = Places.createClient(this);
        mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
    }

Adicionar código no menu da barra de apps

Esses dois métodos adicionam o menu da barra de apps (com um único item, o ícone Escolher lugar) e lidam com o clique do usuário no ícone.

Copie esses dois métodos no seu arquivo após o onCreate.

MapsActivity.java onCreateOptionsMenu() e onOptionsItemSelected()

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
           case R.id.action_geolocate:

                // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Present the current place picker
                // pickCurrentPlace();
                return true;

            default:
                // If we got here, the user's action was not recognized.
                // Invoke the superclass to handle it.
                return super.onOptionsItemSelected(item);

        }
    }

Realizar o teste

  1. No Android Studio, clique em Run ou Run menu > Run ‘app'.

28bea91c68c36fb2.png

  1. Você precisará selecionar o destino de implantação. O emulador em execução aparecerá nessa lista. Selecione-o, e o Android Studio implantará o app no emulador por você.

f44658ca91f6f41a.png

Depois de alguns momentos, o app será iniciado. Você verá o mapa centralizado em Sydney, Austrália, com o único botão e a lista de lugares não preenchida.

68eb8c70f4748350.png

O foco do mapa não se move para o local do usuário, a menos que você solicite permissão para acessar a localização do dispositivo.

10. Solicitar e processar permissões de localização

Solicitar permissões de localização depois que o mapa estiver pronto

  1. Defina um método chamado getLocationPermission que solicita permissões do usuário.

Cole este código abaixo do método onOptionsSelected que você acabou de criar.

MapsActivity.java getLocationPermission()

    private void getLocationPermission() {
        /*
         * Request location permission, so that we can get the location of the
         * device. The result of the permission request is handled by a callback,
         * onRequestPermissionsResult.
         */
        mLocationPermissionGranted = false;
        if (ContextCompat.checkSelfPermission(this.getApplicationContext(),
                android.Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            mLocationPermissionGranted = true;
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
        }
    }
  1. Adicione duas linhas ao fim do método onMapReady existente para ativar os controles de zoom e solicitar permissões de localização ao usuário.

MapsActivity.java onMapReady()

   @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        // Add a marker in Sydney and move the camera
        LatLng sydney = new LatLng(-34, 151);
        mMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));
        mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));

        //
        // PASTE THE LINES BELOW THIS COMMENT
        //

        // Enable the zoom controls for the map
        mMap.getUiSettings().setZoomControlsEnabled(true);

        // Prompt the user for permission.
        getLocationPermission();

    }

Lidar com o resultado das permissões solicitadas

Quando o usuário responde à caixa de diálogo de solicitação de permissão, esse callback é feito pelo Android.

Cole este código após o método getLocationPermission():

MapsActivity.java onRequestPermissionsResult()

   /**
     * Handles the result of the request for location permissions
     */
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        mLocationPermissionGranted = false;
        switch (requestCode) {
            case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    mLocationPermissionGranted = true;
                }
            }
        }
    }

11. Acessar o local atual e buscar locais prováveis

Quando o usuário clica em Escolher lugar na barra de apps, o app chama o método pickCurrentPlace() que, por sua vez, invoca o método getDeviceLocation() definido anteriormente. O método getDeviceLocation chama outro, o getCurrentPlaceLikelihoods, depois de recuperar o local mais recente do dispositivo.

Chamar a API findCurrentPlace e processar a resposta

O getCurrentPlaceLikelihoods constrói um findCurrentPlaceRequest e chama a tarefa findCurrentPlace da API Places. Se a tarefa for bem-sucedida, ela retornará um findCurrentPlaceResponse, que contém uma lista de objetos placeLikelihood. Cada uma delas tem diversas propriedades, incluindo o nome e o endereço do lugar e a probabilidade de você estar nele (um valor duplo de 0 a 1). Esse método lida com a resposta criando listas de detalhes do lugar do placeLikelihoods.

Este código faz uma iteração com os cinco lugares mais prováveis e os adiciona aqueles com um grau de probabilidade maior que 0 a uma lista onde eles são renderizados. Para exibir mais ou menos de cinco itens, edite a constante M_MAX_ENTRIES.

Cole este código após o método onMapReady.

MapsActivity.java getCurrentPlaceLikelihoods()

   private void getCurrentPlaceLikelihoods() {
        // Use fields to define the data types to return.
        List<Place.Field> placeFields = Arrays.asList(Place.Field.NAME, Place.Field.ADDRESS,
                Place.Field.LAT_LNG);

        // Get the likely places - that is, the businesses and other points of interest that
        // are the best match for the device's current location.
        @SuppressWarnings("MissingPermission") final FindCurrentPlaceRequest request =
                FindCurrentPlaceRequest.builder(placeFields).build();
        Task<FindCurrentPlaceResponse> placeResponse = mPlacesClient.findCurrentPlace(request);
        placeResponse.addOnCompleteListener(this,
                new OnCompleteListener<FindCurrentPlaceResponse>() {
                    @Override
                    public void onComplete(@NonNull Task<FindCurrentPlaceResponse> task) {
                        if (task.isSuccessful()) {
                            FindCurrentPlaceResponse response = task.getResult();
                            // Set the count, handling cases where less than 5 entries are returned.
                            int count;
                            if (response.getPlaceLikelihoods().size() < M_MAX_ENTRIES) {
                                count = response.getPlaceLikelihoods().size();
                            } else {
                                count = M_MAX_ENTRIES;
                            }

                            int i = 0;
                            mLikelyPlaceNames = new String[count];
                            mLikelyPlaceAddresses = new String[count];
                            mLikelyPlaceAttributions = new String[count];
                            mLikelyPlaceLatLngs = new LatLng[count];

                            for (PlaceLikelihood placeLikelihood : response.getPlaceLikelihoods()) {
                                Place currPlace = placeLikelihood.getPlace();
                                mLikelyPlaceNames[i] = currPlace.getName();
                                mLikelyPlaceAddresses[i] = currPlace.getAddress();
                                mLikelyPlaceAttributions[i] = (currPlace.getAttributions() == null) ?
                                        null : TextUtils.join(" ", currPlace.getAttributions());
                                mLikelyPlaceLatLngs[i] = currPlace.getLatLng();

                                String currLatLng = (mLikelyPlaceLatLngs[i] == null) ?
                                        "" : mLikelyPlaceLatLngs[i].toString();

                                Log.i(TAG, String.format("Place " + currPlace.getName()
                                        + " has likelihood: " + placeLikelihood.getLikelihood()
                                        + " at " + currLatLng));

                                i++;
                                if (i > (count - 1)) {
                                    break;
                                }
                            }

                            // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                            // Populate the ListView
                            // fillPlacesList();
                        } else {
                            Exception exception = task.getException();
                            if (exception instanceof ApiException) {
                                ApiException apiException = (ApiException) exception;
                                Log.e(TAG, "Place not found: " + apiException.getStatusCode());
                            }
                        }
                    }
                });
    }

Mover a câmera do mapa para a localização atual do dispositivo

Se o usuário concede permissão, o app busca a localização mais recente e move a câmera para centralizar esse lugar.

Se ele negar a permissão, o app simplesmente moverá a câmera para o local padrão definido entre as constantes no início desta página (no exemplo de código, em Sydney, Austrália).

Cole este código após o método getPlaceLikelihoods():

MapsActivity.java getDeviceLocation()

    private void getDeviceLocation() {
        /*
         * Get the best and most recent location of the device, which may be null in rare
         * cases when a location is not available.
         */
        try {
            if (mLocationPermissionGranted) {
                Task<Location> locationResult = mFusedLocationProviderClient.getLastLocation();
                locationResult.addOnCompleteListener(this, new OnCompleteListener<Location>() {
                    @Override
                    public void onComplete(@NonNull Task<Location> task) {
                        if (task.isSuccessful()) {
                            // Set the map's camera position to the current location of the device.
                            mLastKnownLocation = task.getResult();
                            Log.d(TAG, "Latitude: " + mLastKnownLocation.getLatitude());
                            Log.d(TAG, "Longitude: " + mLastKnownLocation.getLongitude());
                            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
                                    new LatLng(mLastKnownLocation.getLatitude(),
                                            mLastKnownLocation.getLongitude()), DEFAULT_ZOOM));
                        } else {
                            Log.d(TAG, "Current location is null. Using defaults.");
                            Log.e(TAG, "Exception: %s", task.getException());
                            mMap.moveCamera(CameraUpdateFactory
                                    .newLatLngZoom(mDefaultLocation, DEFAULT_ZOOM));
                        }

                       getCurrentPlaceLikelihoods();
                    }
                });
            }
        } catch (SecurityException e)  {
            Log.e("Exception: %s", e.getMessage());
        }
    }

Verificar as permissões de localização quando o usuário clicar em "Escolher lugar"

Quando o usuário toca em Escolher lugar, esse método verifica as permissões de localização e solicita a permissão do usuário se ela não tiver sido concedida.

Se o usuário tiver concedido permissão, o método chamará getDeviceLocation para iniciar o processo de busca dos locais atuais.

  1. Adicione este método após getDeviceLocation():

MapsActivity.java pickCurrentPlace()

   private void pickCurrentPlace() {
        if (mMap == null) {
            return;
        }

        if (mLocationPermissionGranted) {
            getDeviceLocation();
        } else {
            // The user has not granted permission.
            Log.i(TAG, "The user did not grant location permission.");

            // Add a default marker, because the user hasn't selected a place.
            mMap.addMarker(new MarkerOptions()
                    .title(getString(R.string.default_info_title))
                    .position(mDefaultLocation)
                    .snippet(getString(R.string.default_info_snippet)));

            // Prompt the user for permission.
            getLocationPermission();
        }
    }
  1. Agora que pickCurrentPlace está definido, encontre a linha em onOptionsItemSelected() que chama pickCurrentPlace e remova a marca de comentário.

MapsActivity.java onOptionItemSelected()

           case R.id.action_geolocate:

                // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Present the Current Place picker
                pickCurrentPlace();
                return true;

Realizar o teste

Se você executar o app agora e tocar em Escolher lugar, ele vai solicitar as permissões de localização.

  • Se você concedeu permissão, essa preferência será salva e você não receberá a solicitação. Caso tenha recusado, receberá uma solicitação na próxima vez que tocar nesse botão.
  • Embora getPlaceLikelihoods tenha recuperado os prováveis locais atuais, o ListView não os exibe ainda. No Android Studio, você pode clicar em ⌘6 para consultar os registros no Logcat, ver as instruções marcadas em MapsActivity e conferir se os novos métodos estão funcionando corretamente.
  • Se você concedeu permissão, os registros incluem uma instrução para Latitude: e uma para Longitude: mostrando o local detectado do dispositivo. Caso tenha usado o Google Maps e o menu estendido do emulador antes de especificar um local, essas instruções mostram essa localização.
  • Se a chamada para findCurrentPlace for bem-sucedida, os registros incluirão cinco instruções que exibem os nomes e localizações dos cinco locais mais prováveis.

d9896a245b81bf3.png

12. Preencher o seletor de lugar atual

Configurar um gerenciador para lugares selecionados

Vamos ver o que acontecerá quando o usuário clicar em um item na ListView. Para confirmar a escolha do usuário sobre o lugar onde ele está no momento, adicione um marcador ao mapa. Se o usuário clicar nesse marcador, uma janela de informações será exibida, indicando o nome e o endereço do lugar.

Cole este gerenciador de cliques após o método pickCurrentPlace.

MapsActivity.java listClickedHandler

    private AdapterView.OnItemClickListener listClickedHandler = new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView parent, View v, int position, long id) {
            // position will give us the index of which place was selected in the array
            LatLng markerLatLng = mLikelyPlaceLatLngs[position];
            String markerSnippet = mLikelyPlaceAddresses[position];
            if (mLikelyPlaceAttributions[position] != null) {
                markerSnippet = markerSnippet + "\n" + mLikelyPlaceAttributions[position];
            }

            // Add a marker for the selected place, with an info window
            // showing information about that place.
            mMap.addMarker(new MarkerOptions()
                    .title(mLikelyPlaceNames[position])
                    .position(markerLatLng)
                    .snippet(markerSnippet));

           // Position the map's camera at the location of the marker.
            mMap.moveCamera(CameraUpdateFactory.newLatLng(markerLatLng));
        }
    };

Preencher o ListView

Agora que você tem a lista de lugares mais prováveis que o usuário está visitando no momento, é possível apresentar essas opções para ele no ListView. Também é possível definir o listener de clique ListView para usar o gerenciador de cliques que você acabou de definir.

Cole este método após o gerenciador de clique:

MapsActivity.java fillPlacesList()

    private void fillPlacesList() {
        // Set up an ArrayAdapter to convert likely places into TextViews to populate the ListView
        ArrayAdapter<String> placesAdapter =
                new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mLikelyPlaceNames);
        lstPlaces.setAdapter(placesAdapter);
        lstPlaces.setOnItemClickListener(listClickedHandler);
    }

Agora que fillPlacesList está definido, encontre a linha no fim de findPlaceLikelihoods, chamada fillPlacesList, e remova o comentário.

MapsActivity.java fillPlaceLikelihoods()

               // COMMENTED OUT UNTIL WE DEFINE THE METHOD
                // Populate the ListView
                fillPlacesList();

Esse é o código necessário para o seletor de local atual.

13. Executar o app

Testar a escolha de lugar

  1. Execute o app novamente.

Dessa vez, quando você tocar em Escolher lugar, o app preenche a lista com lugares nomeados próximos ao local. Perto desse lugar em Maui estão locais como o Hawaiian Shave Ice e o Sugar Beach Bake Shop de Ululani. Como os diversos lugares estão muito próximos das coordenadas selecionada, essa é uma lista de possíveis lugares.

  1. Clique no nome de um local no ListView.

Você verá um marcador adicionado ao mapa.

  1. Toque no marcador.

Você verá detalhes do local.

e52303cc0de6a513.png 864c74342fb52a01.png

Testar outro lugar

Se você quiser alterar sua localização e estiver usando o emulador, a localização do dispositivo não será atualizada automaticamente quando as coordenadas de local forem atualizadas no menu estendido do emulador.

Para contornar isso, siga estas etapas para usar o app Google Maps nativo para forçar atualizações do local do emulador:

  1. Abra o Google Maps.
  2. Toque em ... > Localização para alterar a latitude e a longitude com as novas coordenadas e toque em Enviar.
  3. Por exemplo, você pode usar latitude: 49.2768 e longitude: -123.1142 para definir o local como o centro de Vancouver, Canadá.
  4. Verifique se o Google Maps centralizou de acordo com suas novas coordenadas. Talvez seja necessário tocar no botão Meu local do app Google Maps para solicitar a centralização.
  5. Volte ao app Current Place e toque em Escolher lugar para ver o mapa nas novas coordenadas e uma nova prováveis locais atuais.

9adb99d1ce25c184.png

Pronto! Você criou um app simples que verifica os lugares no local atual e oferece uma probabilidade de onde você está. Aproveite!

Agora, execute o app com as modificações que você fez para concluir essa etapa bônus.

14. Próximas etapas

Para impedir o roubo da sua chave de API, é necessário protegê-la para que somente o app Android possa usá-la. Se ela ficar sem restrições, qualquer pessoa com sua chave poderá usá-la para chamar as APIs da Plataforma Google Maps e gerar cobranças.

Acessar seu certificado SHA-1

Você precisará dele posteriormente, quando restringir suas chaves de API. Veja a seguir um conjunto de instruções para acessar o certificado de depuração.

Para Linux ou macOS, abra uma janela do terminal e digite o seguinte:

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

Para Windows Vista e Windows 7, execute o seguinte comando:

keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android

Será exibida uma saída semelhante a esta:

Alias name: androiddebugkey
Creation date: Jan 01, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 4aa9b300
Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01 18:04:04 PST 2033
Certificate fingerprints:
     MD5:  AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9
     SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75
     Signature algorithm name: SHA1withRSA
     Version: 3

A linha que começa com SHA1 inclui a impressão digital SHA-1 do certificado. Essa impressão é uma sequência de 20 números hexadecimais de dois dígitos separados por dois pontos.

Quando estiver tudo pronto para lançar um app, siga as instruções desta documentação para recuperar o certificado de lançamento.

Adicionar restrições à sua chave de API

  1. No Console do Cloud, acesse APIs e serviços > Credenciais.

A chave usada para esse app estará incluída na lista Chaves de API.

  1. Clique em 6454a04865d551e6.png para editar as configurações da chave.

316b052c621ee91c.png

  1. Na página da chave de API, após Restrições da chave, faça o seguinte para definir as Restrições do aplicativo:
  2. Selecione Apps Android e siga as instruções.
  3. Clique em Adicionar um item.
  4. Digite o nome do pacote e a impressão digital do certificado SHA-1 (recuperado na seção anterior).

Exemplo:

com.google.codelab.currentplace
BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75s
  1. Para maior proteção, faça o seguinte para definir as restrições da API.
  2. Após as restrições da API, escolha Restringir chave.
  3. Selecione o SDK do Maps para Android e a API Places.
  4. Clique em Concluído e Salvar.

15. Parabéns

Você criou um app simples que verifica os lugares mais prováveis no local atual e adiciona um marcador ao mapa para o local selecionado pelo usuário.

Saiba mais