Создайте свой собственный выбор текущего места для Android (Java)

1. Прежде чем начать

Узнайте, как использовать платформу Google Карт и Places SDK для Android, чтобы предоставить пользователям список мест для определения их текущего местоположения.

bd07a9ad2cb27a06.png

Предпосылки

  • Базовые навыки Java

Что ты будешь делать?

  • Добавьте карту в приложение для Android.
  • Используйте разрешения на определение местоположения для геолокации пользователя.
  • Поиск мест рядом с текущим местоположением пользователя.
  • Представить пользователю вероятные места для определения его текущего местоположения.

Что вы построите

Вы создаёте своё Android-приложение с нуля, но можете скачать пример кода для сравнения при отладке. Загрузите пример кода с GitHub или, если у вас настроен Git для работы из командной строки, введите следующее:

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

Если во время работы над этой лабораторной работой у вас возникнут какие-либо проблемы (ошибки кода, грамматические ошибки, неясные формулировки и т. д.), сообщите о них, воспользовавшись ссылкой «Сообщить об ошибке» в левом нижнем углу лабораторной работы.

2. Начните

Перед началом выполнения этой лабораторной работы вам необходимо настроить следующее:

Android Studio

Загрузите Android Studio с сайта https://developer.android.com/studio .

Если у вас уже установлен Android Studio, убедитесь, что у вас установлена последняя версия, нажав Android Studio > Проверить обновления... .

1f36bae83b64e33.png

Эта лабораторная работа была написана с использованием Android Studio 3.4.

Android SDK

В Android Studio вы можете настроить необходимые SDK с помощью SDK Manager. В этой лабораторной работе используется Android Q SDK.

  1. На экране приветствия Android Studio нажмите Настроить > Менеджер SDK .

d3fa03c269ec231c.png

  1. Установите флажок нужного вам SDK, затем нажмите «Применить» .

Если у вас еще нет SDK, начнется загрузка SDK на ваш компьютер.

884e0aa1314f70d.png

Сервисы Google Play

Из менеджера SDK вам также необходимо установить сервисы Google Play.

  1. Откройте вкладку «Инструменты SDK» и установите флажок «Сервисы Google Play» .

Обновите, если статус показывает Доступно обновление .

ad6211fd78f3b629.png

3. Подготовьте эмулятор.

Чтобы запустить приложение, вы можете подключить свое устройство или использовать эмулятор Android.

Если вы используете собственное устройство, перейдите к разделу «Инструкции для реального устройства: обновление сервисов Google Play» в конце этой страницы.

Добавить эмулятор

  1. На экране приветствия Android Studio нажмите Настроить > AVD Manager .

5dd2d14c9c56d3f9.png

Откроется диалоговое окно диспетчера виртуальных устройств Android .

  1. Нажмите «Создать виртуальное устройство...» , чтобы открыть список устройств, которые вы можете выбрать.

2d44eada384f8b35.png

  1. Выберите устройство с Play d5722488d80cd6be.png значок в столбце Play Маркет и нажмите Далее .

e0248f1c6e85ab7c.png

Вы увидите набор образов системы, которые можно установить. Если рядом с версией Q для Android 9.+ (Google Play) есть кнопка «Загрузить» , нажмите «Загрузить» .

316d0d1efabd9f24.png

  1. Нажмите «Далее» , чтобы дать имя вашему виртуальному устройству, затем нажмите « Готово» .

Вы возвращаетесь к списку Ваших виртуальных устройств .

  1. Нажмите «Пуск». ba8adffe56d3b678.png рядом с вашим новым устройством:

7605864ed27f77ea.png

Через несколько мгновений эмулятор откроется.

Инструкции для эмулятора — обновление сервисов Google Play

  1. После запуска эмулятора нажмите ... на появившейся панели навигации**.**

2e1156e02643d018.png

Откроется диалоговое окно «Расширенные элементы управления» .

  1. Нажмите Google Play в меню.

Если доступно обновление, нажмите Обновить .

5afd2686c5cad0e5.png

  1. Войдите в эмулятор, используя учетную запись Google.

Вы можете использовать свою собственную учетную запись или бесплатно создать новую, чтобы отделить результаты тестирования от ваших персональных данных.

Затем Google Play открывает сервисы Google Play.

  1. Нажмите «Обновить» , чтобы получить последнюю версию сервисов Google Play.

f4bc067e80630b9c.png

Если вам будет предложено завершить настройку учетной записи и добавить способ оплаты, нажмите «Пропустить» .

Установить местоположение в эмуляторе

  1. После запуска эмулятора введите «карты» в строку поиска на главном экране, чтобы открыть значок приложения Google Maps.

2d996aadd53685a6.png

  1. Нажмите на значок для запуска.

Вы видите карту по умолчанию.

  1. В правом нижнем углу карты нажмите «Ваше местоположение» . c5b4e2fda57a7e71.png .

Вас попросят предоставить телефону разрешение на использование данных о местоположении.

f2b68044eabca151.png

  1. Нажмите ... , чтобы открыть меню расширенных элементов управления .
  2. Откройте вкладку Местоположение .
  3. Введите широту и долготу.

Введите сюда все, что вам нравится, но убедитесь, что это место находится в районе, где много других мест.

(Используйте широту 20,7818 и долготу -156,4624 для города Кихеи на острове Мауи, Гавайи, чтобы воспроизвести результаты этой лабораторной работы.)

  1. Нажмите «Отправить» , и карта обновится с указанием этого местоположения.

f9576b35218f4187.png

Теперь вы готовы запустить свое приложение и протестировать его с учетом местоположения.

Инструкции для реального устройства — обновление сервисов Google Play

Если вы используете настоящее Android-устройство, то сделайте следующее:

  1. Используйте строку поиска на главном экране для поиска и открытия сервисов Google Play.
  2. Нажмите «Подробнее» .

Если доступно, нажмите Обновить .

ad16cdb975b5c3f7.pngbaf0379ef8a9c88c.png

4. Создайте оболочку приложения с активностью Google Maps.

  1. На экране приветствия Android Studio выберите Начать новый проект Android Studio .
  2. На вкладке «Телефон и планшет» выберите «Действия в Google Картах» .

c9c80aa8211a8761.png

Откроется диалоговое окно « Настройка проекта» . Здесь вы можете указать имя своего приложения и создать пакет на основе своего домена.

Ниже приведены настройки приложения Current Place, которое соответствует пакету com.google.codelab.currentplace .

37f5b93b94ee118c.png

  1. Выберите Java в качестве языка и выберите Использовать androidx. artifacts *.

Для остальных настроек оставьте значения по умолчанию.

  1. Нажмите кнопку Готово .

5. Добавьте зависимости Google Services в файл сборки Gradle.

Для доступа к данным о местоположении в Android вам потребуется API Google Location and Activity Recognition из сервисов Google Play. Подробнее о добавлении этого и других API сервисов Google Play см. в разделе «Настройка сервисов Google Play» .

Проекты Android Studio обычно содержат два файла build.gradle . Один предназначен для всего проекта, а другой — для приложения. Если вы используете проводник проектов Android Studio в режиме Android , вы увидите оба файла в папке Gradle Scripts . Чтобы добавить сервисы Google, необходимо отредактировать файл build.gradle (Module: app) .

f3043429cf719c47.png

  1. Добавьте две строки в раздел dependencies , чтобы добавить службы Google для определения местоположения и API Places ( пример кода в контексте ).

build.gradle (Модуль: 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. Включите API платформы Google Карт и получите ключ API.

Для следующего шага включения вам необходимо включить Maps SDK для Android и Places API .

Настройте платформу Google Карт

Если у вас еще нет учетной записи Google Cloud Platform и проекта с включенным выставлением счетов, ознакомьтесь с руководством « Начало работы с Google Maps Platform», чтобы создать учетную запись для выставления счетов и проект.

  1. В Cloud Console щелкните раскрывающееся меню проектов и выберите проект, который вы хотите использовать для этой кодовой лаборатории.

  1. Включите API и SDK платформы Google Карт, необходимые для этой лабораторной работы, в Google Cloud Marketplace . Для этого следуйте инструкциям в этом видео или в этой документации .
  2. Сгенерируйте ключ API на странице «Учётные данные» в Cloud Console. Вы можете следовать инструкциям в этом видео или в этой документации . Для всех запросов к платформе Google Карт требуется ключ API.

Скопируйте только что созданный ключ API. Вернитесь в Android Studio и найдите файл google_maps_api.xml в разделе Android > app > res > value s.

Замените YOUR_KEY_HERE на скопированный вами ключ API.

aa576e551a7a1009.png

Теперь ваше приложение настроено.

7. Отредактируйте файл макета.

  1. В обозревателе проектов откройте файл activity_maps.xml в папке Android > app > res > layout .

4e0d986480c57efa.png

  1. В правой части экрана откроется базовый интерфейс с вкладками внизу, позволяющими выбрать редактор «Дизайн» или «Текст» для вашего макета. Выберите «Текст» и замените всё содержимое файла макета следующим:

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>

Это даст вам пользовательский интерфейс, который будет выглядеть следующим образом:

1bf786808a4697ce.png

8. Настройте панель приложений.

Чтобы дать пользователю кнопку, по которой он может выбрать своё текущее место, добавьте панель приложения со значком, которая находит текущее место пользователя и отображает близлежащие вероятные места. Это будет выглядеть так:

3a17c92b613a26c5.png

На телефоне отображается только значок. На планшете с большим пространством отображается и текст.

Создать значок

  1. В обозревателе проектов нажмите Android > app , затем щелкните правой кнопкой мыши папку res и выберите New > Image Asset .

Открывается Студия Активов .

  1. В меню «Тип значка» выберите «Значки панели действий и вкладок» .
  2. Назовите свой актив ic_geolocate .
  3. Выберите Клип-арт в качестве типа актива**.**
  4. Щелкните по изображению рядом с клипартом .

Откроется окно «Выбрать значок» .

  1. Выберите значок.

Вы можете использовать строку поиска, чтобы найти значки, соответствующие вашим намерениям.

  1. Найдите location и выберите значок, соответствующий местоположению.

Значок «Мое местоположение» такой же, как тот, который используется в приложении Google Maps, когда пользователь хочет прикрепить камеру к своему текущему местоположению.

  1. Нажмите «ОК» > «Далее» > «Готово» и убедитесь, что создана новая папка с именем drawable , содержащая новые файлы значков.

b9e0196137ed18ae.png

Добавить строковые ресурсы

  1. В обозревателе проектов выберите Android > app > res > values и откройте файл strings.xml .
  2. Добавьте следующие строки после <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>

Первая строка используется на панели приложения, когда рядом со значком есть место для текстовой подписи. Остальные строки используются для маркеров, добавляемых на карту.

Теперь код в файле выглядит так:

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

Добавить панель приложений

  1. В обозревателе проектов нажмите Android > app , затем щелкните правой кнопкой мыши папку res и выберите New > Directory , чтобы создать новый подкаталог в app/src/main/res .
  2. Назовите menu каталога.
  3. Щелкните правой кнопкой мыши папку menu и выберите Создать > Файл .
  4. Назовите файл menu.xml .
  5. Вставьте этот код:
<?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>

Обновите стиль панели приложений

  1. В обозревателе проектов разверните Android > app > res > values и откройте файл styles.xml внутри.
  2. В теге <style> измените родительское свойство на "Theme.AppCompat.NoActionBar" .
  3. Запишите свойство name , которое вы будете использовать на следующем шаге.

стили.xml

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

Обновите тему приложения в AndroidManifest.xml

  1. Нажмите Android > app > manifests и откройте файл AndroidManifest.xml .
  2. Найдите строку android:theme и измените или подтвердите значение на @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">

Теперь вы готовы приступить к программированию!

9. Инициализируйте приложение.

  1. В обозревателе проектов найдите файл MapsActivity.java .

Он находится в папке, соответствующей пакету, который вы создали для своего приложения на шаге 1.

8b0fa27d417f5f55.png

  1. Откройте файл, и вы окажетесь в редакторе кода Java.

Импортируйте Places SDK и другие зависимости

Добавьте эти строки в начало MapsActivity.java , заменив существующие операторы импорта.

Они включают в себя существующие импорты и добавляют множество новых, используемых в коде в этой лабораторной работе.

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;

Обновить сигнатуру класса

API Places использует компоненты AndroidX для поддержки обратной совместимости, поэтому необходимо определить его как расширение AppCompatActivity . Оно заменяет расширение FragmentActivity , которое определено по умолчанию для действия «Карты».

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback {

Добавить переменные класса

Затем объявите различные переменные класса, используемые в разных методах класса. Они включают элементы пользовательского интерфейса и коды состояния. Они должны располагаться сразу под объявлением переменной для 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;

Обновите метод onCreate

Вам потребуется обновить метод onCreate для обработки разрешений пользователя во время выполнения для служб определения местоположения, настройки элементов пользовательского интерфейса и создания клиента API Places.

Добавьте следующие строки кода, касающиеся панели инструментов действий, настройки представлений и клиента Places, в конец существующего метода onCreate() .

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);
    }

Добавьте код для меню панели вашего приложения

Эти два метода добавляют меню панели приложения (с одним элементом — значком Pick Place) и обрабатывают нажатие пользователем на значок.

Скопируйте эти два метода в свой файл после метода onCreate .

MapsActivity.java onCreateOptionsMenu() и 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);

        }
    }

Проверьте это

  1. В Android Studio выберите пункт Выполнить или меню Выполнить > Запустить 'приложение' .

28bea91c68c36fb2.png

  1. Вам будет предложено выбрать цель развертывания. Запущенный эмулятор должен появиться в этом списке. Выберите его, и Android Studio развернет приложение в эмуляторе.

f44658ca91f6f41a.png

Через несколько мгновений приложение запустится. Вы увидите карту с центром в Сиднее, Австралия, с единственной кнопкой и списком незаселённых мест.

68eb8c70f4748350.png

Фокус карты не переместится на местоположение пользователя, пока вы не запросите разрешение на доступ к местоположению устройства.

10. Запрос и обработка разрешений на местоположение

Запросите разрешение на определение местоположения после того, как карта будет готова

  1. Определите метод getLocationPermission , который запрашивает разрешения пользователя.

Вставьте этот код под методом onOptionsSelected который вы только что создали.

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. Добавьте две строки в конец существующего метода onMapReady , чтобы включить управление масштабированием и запросить у пользователя разрешения на определение местоположения.

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();

    }

Обрабатывать результаты запрошенных разрешений

Когда пользователь отвечает на диалоговое окно запроса разрешения, Android вызывает этот обратный вызов.

Вставьте этот код после метода 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. Получите текущее местоположение и найдите вероятные места.

Когда пользователь нажимает кнопку «Выбрать место» на панели приложения, приложение вызывает метод pickCurrentPlace() , который, в свою очередь, вызывает метод getDeviceLocation() который вы определили ранее. Метод getDeviceLocation вызывает другой метод, getCurrentPlaceLikelihoods, после получения последнего местоположения устройства.

Вызовите API findCurrentPlace и обработайте ответ.

getCurrentPlaceLikelihoods создаёт запрос findCurrentPlaceRequest и вызывает задачу findCurrentPlace API Places. В случае успешного выполнения задачи возвращается ответ findCurrentPlaceResponse , содержащий список объектов placeLikelihood . Каждый из них имеет ряд свойств, включая название и адрес места, а также вероятность того, что вы находитесь в этом месте (значение типа double от 0 до 1). Этот метод обрабатывает ответ, формируя списки информации о месте из placeLikelihoods .

Этот код перебирает пять наиболее вероятных мест и добавляет те, вероятность которых больше 0, в список, который затем отображается. Если вы хотите отобразить больше или меньше пяти мест, отредактируйте константу M_MAX_ENTRIES .

Вставьте этот код после метода 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());
                            }
                        }
                    }
                });
    }

Переместите камеру карты в текущее местоположение устройства.

Если пользователь дает разрешение, приложение получает данные о последнем местоположении пользователя и перемещает камеру в центр этого местоположения.

Если пользователь отказывает в разрешении, приложение просто перемещает камеру в местоположение по умолчанию, определенное среди констант в начале этой страницы (в примере кода это Сидней, Австралия).

Вставьте этот код после метода 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());
        }
    }

Проверяйте разрешения на определение местоположения, когда пользователь нажимает «Выбрать место»

Когда пользователь нажимает «Выбрать место» , этот метод проверяет разрешения на определение местоположения и запрашивает у пользователя разрешение, если оно не было предоставлено.

Если пользователь дал разрешение, то метод вызывает getDeviceLocation , чтобы инициировать процесс получения текущих вероятных мест.

  1. Добавьте этот метод после 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. Теперь, когда pickCurrentPlace определен, найдите строку в onOptionsItemSelected() , которая вызывает pickCurrentPlace , и раскомментируйте ее.

MapsActivity.java onOptionItemSelected()

           case R.id.action_geolocate:

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

Проверьте это

Если вы сейчас запустите приложение и нажмете «Выбрать место» , оно должно запросить разрешение на определение местоположения.

  • Если вы дадите разрешение, эта настройка сохранится, и запрос не будет отображаться. Если вы откажетесь, запрос появится при следующем нажатии кнопки.
  • Хотя getPlaceLikelihoods уже получил данные о вероятных текущих местах, ListView пока их не отображает. В Android Studio вы можете нажать ⌘6 , чтобы проверить журналы Logcat на наличие операторов с тегом MapsActivity и убедиться, что новые методы работают правильно.
  • Если вы предоставили разрешение, в журналах будут отображаться значения Latitude: и « Longitude: , показывающие обнаруженное местоположение устройства. Если ранее вы использовали Google Карты и расширенное меню эмулятора для указания местоположения эмулятора, эти значения будут содержать это местоположение.
  • Если вызов findCurrentPlace был успешным, журналы включают пять операторов, печатающих названия и местоположения пяти наиболее вероятных мест.

d9896a245b81bf3.png

12. Заполните поле выбора текущего места.

Настройте обработчик для выбранных мест

Давайте подумаем о том, что должно происходить, когда пользователь щёлкает по элементу в ListView . Чтобы подтвердить выбор текущего места, можно добавить на карту маркер в этом месте. Если пользователь щёлкнет по этому маркеру, появится информационное окно с названием и адресом места.

Вставьте этот обработчик щелчков после метода 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));
        }
    };

Заполнить ListView

Теперь, когда у вас есть список наиболее вероятных мест, которые пользователь в данный момент посещает, вы можете представить эти варианты пользователю в ListView . Вы также можете настроить прослушиватель щелчков ListView на использование обработчика щелчков, который вы только что определили.

Вставьте этот метод после обработчика щелчков:

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);
    }

Теперь, когда fillPlacesList определен, найдите строку в конце findPlaceLikelihoods , которая вызывает fillPlacesList , и раскомментируйте ее.

MapsActivity.java fillPlaceLikelihoods()

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

Это весь код, необходимый для выбора текущего места!

13. Запустите приложение.

Тестовый выбор места

  1. Запустите приложение еще раз.

На этот раз, когда вы нажмете «Выбрать место» , приложение заполнит список названных мест поблизости. Неподалеку от этого места на Мауи находятся такие заведения, как Ululani's Hawaiian Shave Ice и Sugar Beach Bake Shop. Поскольку несколько заведений находятся очень близко к координатам местоположения, это список вероятных мест, где вы можете находиться.

  1. Щелкните по названию места в ListView .

Вы должны увидеть маркер, добавленный на карту.

  1. Нажмите на маркер.

Вы можете увидеть подробную информацию о месте.

e52303cc0de6a513.png864c74342fb52a01.png

Проверьте другое место

Если вы хотите изменить свое местоположение и используете эмулятор, местоположение устройства не обновляется автоматически при обновлении координат местоположения в расширенном меню эмулятора.

Чтобы обойти эту проблему, выполните следующие действия, чтобы использовать собственное приложение Google Maps для принудительного обновления местоположения эмулятора:

  1. Откройте Google Карты.
  2. Нажмите ... > Местоположение , чтобы изменить широту и долготу на новые координаты, затем нажмите Отправить .
  3. Например, вы можете использовать широту: 49.2768 и долготу: -123.1142, чтобы указать местоположение в центре Ванкувера, Канада.
  4. Убедитесь, что Google Карты центрировались по вашим новым координатам. Возможно, вам потребуется нажать кнопку «Моё местоположение» в приложении Google Карты, чтобы запросить центрирование.
  5. Вернитесь в приложение «Текущее место» и нажмите «Выбрать место», чтобы отобразить карту с новыми координатами и увидеть новый список вероятных текущих мест.

9adb99d1ce25c184.png

Вот и всё! Вы создали простое приложение, которое проверяет места в вашем текущем местоположении и даёт вам оценку вероятности того, где вы находитесь. Наслаждайтесь!

А теперь запустите приложение с внесенными вами изменениями, чтобы завершить этот бонусный шаг!

14. Дальнейшие шаги

Чтобы предотвратить кражу вашего API-ключа, необходимо защитить его так, чтобы его могло использовать только ваше Android-приложение. Если оставить его без присмотра, любой, кто владеет вашим ключом, может использовать его для вызова API платформы Google Карт и списать с вас средства.

Получите сертификат SHA-1

Это понадобится вам позже, когда вы ограничите доступ к своим API-ключам. Ниже приведены инструкции по получению отладочного сертификата.

Для Linux или macOS откройте окно терминала и введите следующее:

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

Для Windows Vista и Windows 7 выполните следующую команду:

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

Вы должны увидеть примерно такой вывод:

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

Строка, начинающаяся с SHA1, содержит отпечаток сертификата SHA-1. Отпечаток представляет собой последовательность из 20 двузначных шестнадцатеричных чисел, разделённых двоеточиями.

Когда вы будете готовы выпустить приложение, воспользуйтесь инструкциями в этой документации , чтобы получить сертификат выпуска.

Добавьте ограничения к вашему ключу API

  1. В Cloud Console перейдите в раздел API и службы > Учетные данные .

Ключ, который вы использовали для этого приложения, должен быть указан в разделе «Ключи API».

  1. Щелкните 6454a04865d551e6.png для редактирования основных настроек.

316b052c621ee91c.png

  1. На странице ключа API, после ограничений ключа , задайте ограничения приложения , выполнив следующие действия:
  2. Выберите приложения Android и следуйте инструкциям.
  3. Нажмите Добавить элемент .
  4. Введите имя вашего пакета и отпечаток сертификата SHA-1 (полученный в предыдущем разделе).

Например:

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. Для дополнительной защиты установите ограничения API , выполнив следующие действия.
  2. После ограничений API выберите Ограничить ключ .
  3. Выберите Maps SDK для Android и Places API.
  4. Нажмите «Готово» и «Сохранить».

15. Поздравления

Вы создали простое приложение, которое проверяет наиболее вероятные места в текущем местоположении и добавляет на карту маркер для места, выбранного пользователем.

Узнать больше