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

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

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

bd07a9ad2cb27a06.png

Предпосылки

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

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

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

Что вы будете строить

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

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

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

2. Начать

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

Android-студия

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

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

1f36bae83b64e33.png

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

SDK для Android

В 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» .

Обновите, если статус гласит: Доступно обновление .

ад6211fd78f3b629.png

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

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

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

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

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

5dd2d14c9c56d3f9.png

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

  1. Щелкните Создать виртуальное устройство... , чтобы открыть список доступных для выбора устройств.

2d44eada384f8b35.png

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

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. Нажмите Подробнее .

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

ад16cdb975b5c3f7.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. артефакты *.

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

  1. Нажмите Готово .

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

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

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

f3043429cf719c47.png

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

build.gradle (Модуль: приложение)

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 Maps и получите ключ API.

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

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

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

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

  1. Включите API и SDK платформы Google Maps, необходимые для этой лаборатории кода, в Google Cloud Marketplace . Для этого выполните действия, описанные в этом видео или в этой документации .
  2. Создайте ключ API на странице учетных данных Cloud Console. Вы можете выполнить действия, описанные в этом видео или в этой документации . Для всех запросов к платформе Google Maps требуется ключ 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. Вы увидите основной пользовательский интерфейс, открытый в правой части экрана, с вкладками внизу, позволяющими выбрать редактор дизайна или текста для макета. Выберите Text и замените все содержимое файла макета следующим:

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 .

Откроется Asset Studio .

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

Откроется окно выбора значка .

  1. Выберите иконку.

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

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

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

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

b9e0196137ed18ae.png

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

  1. В проводнике проекта щелкните Android > приложение > res > values ​​и откройте файл strings.xml .
  2. Добавьте следующие строки после <string name="title_activity_maps">Map</string> :

строки.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;

Обновите подпись класса

Places API использует компоненты 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);
    }

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

Эти два метода добавляют меню панели приложения (с одним элементом, значком «Выбрать место») и обрабатывают щелчок пользователя по значку.

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

На этот раз, когда вы нажмете « Выбрать место », приложение заполнит список названными местами рядом с местоположением. Рядом с этим местом на Мауи находятся такие места, как 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 Maps, чтобы запросить повторное центрирование.
  5. Вернитесь в приложение «Текущее место» и нажмите « Выбрать место », чтобы получить карту с новыми координатами и увидеть новый список вероятных текущих мест.

9adb99d1ce25c184.png

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

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

14. Следующие шаги

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

Получите сертификат 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. Поздравления

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

Учить больше