この Codelab について
1. 始める前に
Google Maps Platform と Places SDK for Android を使用して、場所の一覧をユーザーに表示し、ユーザーの現在地を特定する方法を学びます。
要件
- 基本的な Java 操作技術
演習内容
- Android アプリに地図を追加する
- 位置情報の利用許可を使用して、ユーザーの位置情報を取得する
- ユーザーの現在地の周辺にある場所を取得する
- 現在地である可能性の高い場所をユーザーに表示し、ユーザーの現在地を特定する
作成内容
Android アプリを一から作成しますが、デバッグ用のサンプルコードを参照用としてダウンロードできます。GitHub からサンプルコードをダウンロードするか、コマンドライン用に Git が設定されている場合は次のように入力します。
git clone https://github.com/googlecodelabs/current-place-picker-android.git
この Codelab で何か問題(コードのバグ、文法的な誤り、不明確な表現など)が見つかりましたら、Codelab の左下にある [誤りを報告] から報告してください。
2. 作成を開始する
この Codelab を開始する前に、次を設定してください。
Android Studio
Android Studio を https://developer.android.com/studio からダウンロードします。
すでに Android Studio がインストールされている場合は、[Android Studio] > [Check for Updates] をクリックして最新バージョンであることを確認します。
この Codelab は、Android Studio 3.4 を使用して作成されています。
Android SDK
Android Studio では、SDK Manager を使ってご希望の SDK を設定できます。この Codelab では、Android Q SDK を使用します。
- Android Studio の「ようこそ」画面で、[Configure] > [SDK Manager] をクリックします。
- 目的の SDK のチェックボックスをオンにして、[Apply] をクリックします。
パソコンに SDK がない場合は、ダウンロードされます。
Google Play 開発者サービス
SDK Manager から、Google Play 開発者サービスもインストールする必要があります。
- [SDK Tools] タブをクリックし、[Google Play services] チェックボックスをオンにします。
ステータスが [Update available] の場合は更新します。
3. エミュレータを準備する
アプリを実行するには、ご自身のデバイスを接続するか、Android Emulator を使用します。
ご自身のデバイスを使用する場合は、このページの最後の実際のデバイスに関する手順: Google Play 開発者サービスを更新するに進んでください。
エミュレータを追加する
- Android Studio の「ようこそ」画面で、[Configure] > [AVD Manager] をクリックします。
[Android Virtual Device Manager] ダイアログが開きます。
- [Create Virtual Device] をクリックすると、選択可能なデバイスのリストが表示されます。
- [Play Store] 列で再生アイコン
が表示されているデバイスを選択し、[Next] をクリックします。
インストール可能なシステム イメージの一覧が表示されます。Android 9 以降(Google Play)を対象とする Q の横に [Download] という語が表示されている場合は、[Download] をクリックします。
- [Next] をクリックして、仮想デバイスに名前を付け、[Finish] をクリックします。
[Your Virtual Devices] のリストに戻ります。
- 新しいデバイスの横にある開始アイコン
をクリックします。
しばらくすると、エミュレータが開きます。
エミュレータに関する手順 - Google Play 開発者サービスを更新する
- エミュレータが起動したら、表示されるナビゲーション バーで [...] をクリックします**。**
[Extended controls] ダイアログが開きます。
- メニューで [Google Play] をクリックします。
利用可能なアップデートがある場合は、[Update] をクリックします。
- Google アカウントでエミュレータにログインします。
ご自分のアカウントを使用できますが、個人情報をテストに関連付けたくない場合は新しいアカウントを作成(無料)することもできます。
Google Play 開発者サービスが開きます。
- [Update] をクリックして、Google Play 開発者サービスの最新バージョンを入手します。
アカウント設定を完了し、お支払い方法の追加を求められた場合は、[Skip] をクリックします。
エミュレータで位置情報を設定する
- エミュレータが起動したら、ホーム画面の検索バーに「maps」と入力して、Google マップ アプリのアイコンを表示させます。
- アイコンをクリックして起動します。
デフォルトの地図が表示されます。
- 地図の右下にある現在地アイコン
をクリックします。
位置情報の利用権限をスマートフォンに付与するよう求められます。
- [...] をクリックして [Extended Controls] メニューを開きます。
- [Location] タブをクリックします。
- 緯度と経度を入力します。
任意の緯度と経度の値を入力できますが、場所が十分にある地域が表示されるようにしましょう。
(このコードラボの結果を再現するには、緯度 20.7818 と経度 -156.4624 を使用してください。ハワイのマウイ島キヘイが表示されます。)
- [Send] をクリックすると、地図にその位置が表示されます。
これで、アプリを実行してその位置でテストする準備が整いました。
実際のデバイスに関する手順 - Google Play 開発者サービスを更新する
実際の Android デバイスを使用している場合は、次の手順を行います。
- ホーム画面の検索バーを使用して、Google Play 開発者サービスを検索し、開きます。
- [もっと見る] をクリックします。
[更新] をクリックします(表示されている場合)。
4. Google マップ アクティビティでアプリケーション シェルを作成する
- Android Studio の「ようこそ」画面で、[Start a new Android Studio project] を選択します。
- [Phone and Tablet] タブで [Google Maps Activity] を選択します。
[Configure your project] ダイアログが開きます。ここでアプリの名前を指定し、ドメインに基づいてパッケージを作成します。
Current Place と呼ばれるアプリ(パッケージ com.google.codelab.currentplace
に対応する)の設定は次のとおりです。
- 言語として [Java] を選択し、[Use androidx. artifacts] を選択します*。
その他の設定はデフォルトのままにします。
- [完了] をクリックします。
5. Google サービスの依存関係を Gradle ビルドファイルに追加する
Android で位置情報の利用許可を得るには、Google Play 開発者サービスの Google Location and Activity Recognition API が必要です。この API を含む Google Play 開発者サービス API の追加について詳しくは、Google Play 開発者サービスのセットアップをご覧ください。
Android Studio プロジェクトには、通常 2 つの build.gradle
ファイルがあります。1 つはプロジェクト全体用、もう 1 つはアプリ用です。[Android] ビューに Android Studio プロジェクト エクスプローラがある場合、その両方が Gradle Scripts
フォルダに表示されます。Google サービスを追加するには、build.gradle (Module: app)
ファイルを編集する必要があります。
dependencies
セクションに 2 行を追加して、位置情報と Places API(コンテキスト内のサンプルコード)に Google サービスを追加します。
build.gradle(Module: 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. Google Maps Platform の API を有効にして、API キーを取得する
次のステップでは、Maps SDK for Android と Places API を有効にします。
Google Maps Platform を設定する
課金を有効にした Google Cloud Platform アカウントとプロジェクトをまだ作成していない場合は、Google Maps Platform スタートガイドに沿って請求先アカウントとプロジェクトを作成してください。
- Cloud Console で、プロジェクトのプルダウン メニューをクリックし、この Codelab に使用するプロジェクトを選択します。
- Google Cloud Marketplace で、この Codelab に必要な Google Maps Platform API と SDK を有効にします。詳しい手順については、こちらの動画またはドキュメントをご覧ください。
- Cloud Console の [認証情報] ページで API キーを生成します。詳しい手順については、こちらの動画またはドキュメントをご覧ください。Google Maps Platform へのすべてのリクエストで API キーが必要になります。
作成した API キーをコピーします。Android Studio に戻り、[Android] > [app] > [res] > [values] で、ファイル google_maps_api.xml
を見つけます。
YOUR_KEY_HERE
を、コピーした API キーに置き換えます。
これで、アプリの設定が完了しました。
7. レイアウト ファイルを編集する
- プロジェクト エクスプローラで、[Android] > [
app
] > [res
] > [layout
] にあるactivity_maps.xml
ファイルを開きます。
- 画面の右側に基本の UI が表示されます。下部のタブで、レイアウト用に [Design] または [Text] のエディタを選択できます。[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>
次のようなユーザー インターフェースが表示されます。
8. アプリバーを設定する
アイコン付きのアプリバーを追加して、ユーザーが現在地を選択する際にクリックできるボタンを提供します。ユーザーがこのアイコンをクリックすると、ユーザーの現在地を特定し、現在地である可能性の高い周辺の場所が表示されます。アプリバーは次のようになります。
スマートフォンでは、アイコンのみが表示されます。スペースに余裕があるタブレットでは、テキストも表示されます。
アイコンを作成する
- プロジェクト エクスプローラで、[Android] > [app] をクリックし、[res] フォルダを右クリックして [New] > [Image Asset] を選択します。
Asset Studio が開きます。
- [Icon Type] メニューで、[Action Bar and Tab Icons] をクリックします。
- アセットに
ic_geolocate
という名前を付けます。 - アセットタイプとして [Clip Art] を選択します**。**
- [Clip Art] の横にある画像をクリックします。
[Select Icon] ウィンドウが開きます。
- アイコンを選択します。
検索バーを使用して、趣旨にあったアイコンを見つけます。
location
を検索して、現在地に関連するアイコンを選択します。
[my location] アイコンは、現在地にカメラをスナップするために Google マップ アプリで使用するアイコンと同じです。
- [OK] > [Next] > [Finish] をクリックして、新しいアイコン ファイルが格納されている、
drawable
という新しいフォルダがあることを確認します。
文字列リソースを追加する
- プロジェクト エクスプローラで、[Android] > [app] > [res] > [values] をクリックして、
strings.xml
ファイルを開きます。 <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>
アプリバーの 1 行目は、アイコンの横にテキストラベルを表示するスペースがある場合に使用されます。もう 1 行は、マップに追加するマーカーに使用されます。
ファイル内のコードは次のようになります。
<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>
アプリバーを追加する
- プロジェクト エクスプローラで [Android] > [app] をクリックして、
res
フォルダを右クリックし、[New] > [Directory] を選択して、app/src/main/res
に新しいサブディレクトリを作成します。 - ディレクトリに
menu
という名前を付けます。 menu
フォルダを右クリックして、[New] > [File] を選択します。- ファイルに
menu.xml
という名前を付けます。 - 次のコードを貼り付けます。
menu.xml
<?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>
アプリバーのスタイルを更新する
- プロジェクト エクスプローラで、[Android] > [
app
] > [res
] > [values
] の順に開き、中にあるファイルstyles.xml
を開きます。 <style>
タグで、親プロパティを"Theme.AppCompat.NoActionBar"
に編集します。name
プロパティをメモします。このプロパティは、次のステップで使用します。
styles.xml
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
AndroidManifest.xml でアプリのテーマを更新する
- [Android] > [
app
] > [manifests
] をクリックして、AndroidManifest.xml
ファイルを開きます。 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. アプリを初期化する
- プロジェクト エクスプローラで、
MapsActivity.java
ファイルを見つけます。
このファイルは、ステップ 1 でアプリ用に作成したパッケージに対応しています。
- ファイルを開くと、Java コードエディタが開きます。
Places SDK とその他の依存関係をインポートする
下記の行を MapsActivity.java
の先頭に追加して、既存の import ステートメントを置き換えます。
これには、既存の import に加え、この Codelab のコードで使用される import も追加されています。
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 {
クラス変数を追加する
次に、異なるクラスメソッドで使用されるさまざまなクラス変数を宣言します。これらには、UI 要素とステータス コードが含まれており、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
メソッドを更新して、UI 要素の設定と Places API クライアントの作成を行う必要があります。
既存の 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);
}
アプリバー メニューのコードを追加する
これら 2 つのメソッドは、アプリバー メニューと 1 つのアイテム([場所を選択] アイコン)を追加し、ユーザーによるアイコンのクリックを処理します。
これらの 2 つのメソッドを、ファイルの 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);
}
}
テスト
- Android Studio から [Run] または [Run menu] > [Run 'アプリ'] をクリックします。
- デプロイの対象を選択するように求められます。実行中のエミュレータがこのリストに表示されます。これを選択すると、アプリがエミュレータにデプロイされます。
しばらくすると、アプリが起動します。1 つのボタンと未入力の場所のリストとともに、オーストラリアのシドニーを中心とした地図が表示されます。
デバイスの位置情報の利用許可をリクエストしない限り、地図はユーザーの位置に移動しません。
10. 位置情報の利用許可をリクエストして処理する
地図の準備ができたら位置情報の利用許可をリクエストする
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);
}
}
- 既存の
onMapReady
メソッドの末尾に 2 行を追加して、ズーム コントロールを有効にし、ユーザーからの位置情報の利用許可をリクエストします。
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,
メソッドを呼び出します。
findCurrentPlace API を呼び出してレスポンスを処理する
getCurrentPlaceLikelihoods
が findCurrentPlaceRequest
を構築して、Places API findCurrentPlace
タスクを呼び出します。タスクが成功すると、findCurrentPlaceResponse
が返されます。これには、placeLikelihood
オブジェクトのリストが含まれます。各オブジェクトには、場所の名前と住所、ユーザーがその場所にいる可能性(0 ~ 1 の倍精度数)など、多くのプロパティが含まれています。このメソッドは、placeLikelihoods
から場所詳細のリストを作成することでレスポンスを処理します。
このコードは、現在地の可能性が最も高い 5 つの場所に対して反復処理を行い、可能性が 0 より高い場所をリストに追加して、レンダリングします。表示する場所の個数を 5 個以外にする場合は、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
が呼び出され、現在地の可能性が高い場所を取得するプロセスが開始します。
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();
}
}
pickCurrentPlace
が定義されたら、pickCurrentPlace
を呼び出してコメント化を解除するonOptionsItemSelected()
の行を見つけます。
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
の呼び出しが成功した場合、現在地である可能性の最も高い 5 つの場所の名前と位置を出力する 5 つのステートメントがログに含まれます。
12. Current Place Picker にデータを読み込む
選択した場所のハンドラを設定する
ユーザーが 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
が定義されたら、fillPlacesList
を呼び出してコメント化を解除する findPlaceLikelihoods
の最後の方に行を見つけます。
MapsActivity.java fillPlaceLikelihoods()
// COMMENTED OUT UNTIL WE DEFINE THE METHOD
// Populate the ListView
fillPlacesList();
これで、Current Place Picker に必要なコードがすべて揃いました。
13. アプリを実行する
場所の選択をテストする
- アプリを再度実行します。
[場所を選択] をタップすると今回は、位置情報に近い名前付きの場所のリストがアプリに表示されます。マウイ島のこの位置の周辺には、Ululani's Hawaiian Shave Ice や Sugar Beach Bake Shop などの場所があります。この位置の座標の非常に近くにある複数の場所が、現在地である可能性の高い場所としてリストに表示されます。
ListView
で場所の名前をクリックします。
地図にマーカーが追加されます。
- マーカーをタップします。
場所の詳細が表示されます。
別の位置をテストする
エミュレータを使用している際に位置を変更する場合、エミュレータの拡張メニューで位置座標を更新してもデバイスの位置情報は自動的には更新されません。
この問題を回避するには、次の手順でネイティブの Google マップ アプリを使用してエミュレータの位置を強制的に更新します。
- Google マップを開きます。
- [...] > [Location] をタップし、緯度と経度の座標を新しい座標に変更して、[Send] をタップします。
- たとえば、緯度: 49.2768、経度: -123.1142 を使用すると、カナダ バンクーバーのダウンタウンに位置情報を設定することができます。
- Google マップの中心が新しい座標に更新されたことを確認します。中心の変更をリクエストするには、Google マップ アプリの [現在地] ボタンをタップします。
- Current Place アプリに戻り、[場所を選択] をタップすると、新しい座標で地図を取得できます。また、現在地である可能性の高い場所の新しいリストが表示されます。
以上で完了です。現在地の近くにある場所を確認して、現在地である可能性の高い場所のリストを提供することができるシンプルなアプリを作成しました。
変更を加えたアプリを実行して、次のボーナス ステップを完了しましょう。
14. 次のステップ
API キーの盗難を防ぐには、ご自身の Android アプリだけがキーを使用できるように保護する必要があります。制限されていないキーがあれば、誰でもそのキーを使って Google Maps Platform API を呼び出し、課金を発生させることができます。
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 個の 2 桁の 16 進数で構成されるシーケンスです。
アプリのリリースの準備が整ったら、こちらのドキュメントの手順に沿ってリリース証明書を取得してください。
API キーに制限を追加する
- Cloud Console で、[API とサービス] > [認証情報] に移動します。
このアプリに使用したキーが [API キー] の下に表示されます。
をクリックして、キーの設定を編集します。
- API キーのページで [キーの制限] の下の [アプリケーションの制限] を設定します。
- [Android アプリ] を選択し、表示される手順に沿って操作します。
- [項目を追加] をクリックします。
- パッケージ名と 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
- さらに保護するには、次の手順に沿って [API の制限] を設定します。
- [API の制限] の下で [キーを制限] を選択します。
- Maps SDK for Android と Places API を選択します。
- [完了]、[保存] の順にクリックします。
15. 完了
現在地の可能性が最も高い場所を確認し、ユーザーが選択した場所のマーカーを地図に追加する簡単なアプリを作成しました。
さらに学びましょう
- 開発を迅速に進めるには、Maps SDK for Android ユーティリティ ライブラリをご利用ください。これらのユーティリティは、Google Maps Platform を使用するアプリで最も一般的なタスクの負担を軽減してくれます。
- Android 向け Google Maps Platform SDK のほとんどの機能を実行するコードサンプルについては、Maps SDK for Android のサンプルおよび Places SDK for Android のデモのリポジトリのクローンを作成します。
- Android Q で位置情報の 3 段階の利用許可を処理する方法を学ぶには、Kotlin を使用して Android で現在地の更新情報を受信する Codelab を完了してください。