Before you begin coding, there are a few prerequisites that you'll need to set up.

Android Studio

Android Studio may be downloaded from http://tools.android.com/download/studio. There are many versions on there, and it is updated all the time. This lab was written using Android Studio 2.1 Preview 1 on the Canary channel.

Android SDK

From Android Studio, you can configure your desired SDKs using the SDK Manager. This lab uses the Android 6.0 (Marshmallow) SDK.

Google Play Services

From the SDK manager, you'll also need to install Google Play services. This can be found in the ‘SDK Tools' tab.

Your Places API Key will give you access to the Places API. You can get this by visiting the Google Places API for Android site. On here you'll see a button that says ‘Get a Key'

When you press it, you'll get a dialog asking you to ‘Activate the Google Places API for Android', detailing the steps:

Press ‘Continue' on this dialog. One of two things will happen. If you have an existing project on the Google Developers Console, you'll be able to select it, or add a new one. If you do not have one already, you'll see a screen like this -- where you will have a new project called ‘My Project' automatically created for you.

If you agree to the terms, select ‘Yes' and then press ‘Agree and continue'. The project will be created for you, and the Places API will be enabled automatically.

You'll then be taken to the ‘Create Android API Key' screen.

You need some information here in order to continue.

The first is your package name. In this lab you'll be creating an app called com.google.codelab.currentplace. If you use a different name for you app be sure to note it, because you'll be entering it on this screen in a moment.

The second is the SHA-1 signing fingerprint for your app. Further details on this are available here. For the purposes of this lab you'll be signing your app as a debug app, which is much easier -- but doesn't allow you to deploy it to the play store. For that you'll need to sign in release mode. To get your SHA-1 debug key, you have to find your debug keystore.

It's in these folders:

On Mac: /Users/<username>/.android/

On Windows: C:\Documents and Settings\<username>\.android\

On Linux: ~/.android/

Once you've found it, open a terminal or command prompt, and from within the same directory as debug.keystore, issue the following command:

keytool -list -v -keystore debug.keystore -storepass android

You'll see an output something like this:

Note the SHA1 value.

Back on Console, click the ‘Add Package name and fingerprint' button, and you'll have a space where you can enter these values, like this:

Click Create, and you'll get a popup with an API key like this:

Take a note of this API key -- you'll be needing it when you write your app!

From Android Studio, select File->New and then pick new project.

You'll see the ‘Create New Project' dialog. Here is where you name your app and create the package based on your domain. Important the package must match what you configured in the Google Developers Console above. If you used a different name than the provided one, be sure to make sure that the package name generated here matches. Here's the settings for an app called CurrentPlace, which corresponds to the package com.google.codelab.currentplace.

Press ‘Next' and you'll be asked for the form factors on which your app will run. Just keep the defaults, and press ‘Next'.

On the following screen you'll be asked to add an activity. Make sure ‘Empty Activity' is selected. Then press ‘Next'.

You'll be taken to the ‘Customize the Activity' dialog. Keep this as default, and press ‘Finish'. Android studio will create the project for you.

Android Studio projects typically have 2 build.gradle files. One is for the overall project, and one is for the app. If you have the Android Studio Project explorer in ‘Android' view, you'll see both of them in the ‘Gradle Scripts' folder, like this:

You'll need to edit the one in the app folder (Module: app in above diagram) to add Google services. Open it, and edit it to add a line to the dependencies section:

apply plugin: 'com.android.application'

android {
   compileSdkVersion 23
   buildToolsVersion "23.0.2"

   defaultConfig {
       applicationId "com.google.codelab.currentplace"
       minSdkVersion 15
       targetSdkVersion 23
       versionCode 1
       versionName "1.0"
   }
   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
       }
   }
}

dependencies {
   compile fileTree(dir: 'libs', include: ['*.jar'])
   testCompile 'junit:junit:4.12'
   compile 'com.android.support:appcompat-v7:23.2.1'
   compile 'com.google.android.gms:play-services:8.4.0'
}

This will include the Google services APIs including the GoogleApiClient and the Places libraries.

To use the places API you'll need to make two edits to your Android manifest file. You'll need to set up the API key that you created earlier, and you'll need to specify the permissions that your app needs.

In Android Studio, in the project explorer's Android view, you can find the manifest within the manifests folder, like this:

Open the file, add the <uses-permission> fields above the <application> tag as shown, then add the API key to the <application> tag:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.google.codelab.currentplace">
   <uses-permission 
      android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>


   <application
       android:allowBackup="true"
       android:icon="@mipmap/ic_launcher"
       android:label="@string/app_name"
       android:supportsRtl="true"
       android:theme="@style/AppTheme">
       <meta-data
           android:name="com.google.android.geo.API_KEY"
           android:value="{ADD YOUR API KEY HERE}"/>

       <activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />

               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
   </application>

</manifest>

Your app is now configured. In the next step you'll edit the layout file containing your UI.

In your project explorer, open the activity_main.xml file in the ‘res' folder:

You'll see the basic UI open on the right of the screen, and there'll be tabs at the bottom allowing you to select the Designer or Text editor for your layout. Select ‘Text', and then replace the entire contents of the layout file with this:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   android:paddingBottom="@dimen/activity_vertical_margin"
   tools:context=".MainActivity"
   android:id="@+id/main_layout"
   android:orientation="vertical"
   android:background="#000000">

   <Button
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Get Current Place"
       android:id="@+id/currentButton"
       android:layout_alignParentTop="true"
       android:layout_centerHorizontal="true">
   </Button>
   <ListView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:id="@+id/listPlaces">
   </ListView>

</LinearLayout>

This will give you a user interface that looks like this:

You're now ready to begin coding!

In your project explorer find the MainActivity file. It will be in the folder corresponding to the package that you created for your app in step 1.

Open it, and you'll be in the Java Code editor.

Update the Class Signature.

The Places API uses the GoogleApiClient, so you need to update your class definition to implement its onConnectionFailedListener -- like this:

public class MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener {

Add Class Variables

Next you'll declare the various class variables that will be used in different class functions. These include the UI elements, and status codes.

private static final String LOG_TAG = "MainActivity";
private static final int GOOGLE_API_CLIENT_ID = 0;
private GoogleApiClient mGoogleApiClient;
private static final int PERMISSION_REQUEST_CODE = 100;
ListView lstPlaces;

Android Studio may ask you to import the requisite files to handle them. If you're stuck, don't worry, all the code, including the imports is available at the end of this lab.

Update your onCreate Function

You'll need to update the onCreate function to handle setting up the UI Elements, creating the GoogleAPIClient and handling runtime user permissions for location services.

Here's the code:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   Button currentButton = (Button) findViewById(R.id.currentButton);
   lstPlaces = (ListView) findViewById(R.id.listPlaces);
   mGoogleApiClient = new GoogleApiClient.Builder(MainActivity.this)
           .addApi(Places.PLACE_DETECTION_API)
           .enableAutoManage(this, GOOGLE_API_CLIENT_ID, this)
           .build();
   currentButton.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
           if (mGoogleApiClient.isConnected()) {
               int hasPermission = checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
               if(hasPermission != PackageManager.PERMISSION_GRANTED){
                   ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_CODE);
               } else {
                   callPlaceDetectionApi();
               }

           }
       }
   });
}

Handle the result from requested permissions.

When the user responds to the request permission dialog, this callback will be called by Android. You'll need to implement it. Here's the code:

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
   switch (requestCode) {
       case PERMISSION_REQUEST_CODE:
           if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
               callPlaceDetectionApi();
           }
           break;
   }
}

In this case if the permission is granted we'll call the place detection API. Otherwise the call will do nothing. It's good user practice to take the user through a flow here telling them the implication of rejecting the permission, but for the sake of brevity we've excluded that here.

Handle onConnectionFailed

The Class signature stated that it handles onConnectionFailed, so you should add that in, or your class won't compile. For brevity, we'll just log it, and make a Toast to tell the user that it failed. In a full app you'd likely have some kind of recovery flow here too.

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
   Log.e(LOG_TAG, "Google Places API connection failed with error code: " + connectionResult.getErrorCode());
   Toast.makeText(this, "Google Places API connection failed with error code:" + connectionResult.getErrorCode(), Toast.LENGTH_LONG).show();
}

Call the Place Detection API

Now that everything is in place, the final step is to call the Place Detection API itself. As documented here -- it will return a set of places near your location with likelihoods that you are in that particular place.

Here's the code:

private void callPlaceDetectionApi() throws SecurityException {
   PendingResult<PlaceLikelihoodBuffer> result = Places.PlaceDetectionApi.getCurrentPlace(mGoogleApiClient, null);
   result.setResultCallback(new ResultCallback<PlaceLikelihoodBuffer>() {
       @Override
       public void onResult(PlaceLikelihoodBuffer likelyPlaces) {
           ArrayAdapter myAdapter = new ArrayAdapter(getApplicationContext(), android.R.layout.simple_list_item_1);
           for (PlaceLikelihood placeLikelihood : likelyPlaces) {
               if(placeLikelihood.getLikelihood()>0){
                   myAdapter.add(placeLikelihood.getPlace().getName().toString());
               }
               Log.i(LOG_TAG, String.format("Place '%s' with " +  "likelihood: %g", placeLikelihood.getPlace().getName(), placeLikelihood.getLikelihood()));
           }
           lstPlaces.setAdapter(myAdapter);
           likelyPlaces.release();
       }
   });
}

First it calls the getCurrentPlace for your location, passing it the API Client. Then, when a result is called back, it will receive a PlaceLikelihoodBuffer containing a list of PlaceLikelihood objects. Each of these have a number of propertied, including the name of the place, and the likelihood probability that you are in that place (a value from 0 to 1) .


This code will iterate through all the places, and add ones with a likelihood > 0 to a list which it will then render.

To run the app, you'll use the Android Emulator. In the next steps you'll set one up, and you'll configure it to use location services.

Configuring the Emulator Image

Let's run the app in the Android Emulator. In Android Studio, go to the Tools Menu, then select Android, then select ‘AVD Manager'.

This brings you to the Android Virtual Device Manager.

At the bottom of this dialog, you'll see a ‘Create Virtual Device..' Button. Select it.

You'll then be given a set of devices you can pick. Select the Nexus 6P and click ‘Next'. You'll see a set of System Images that you can install. If ‘Marshmallow' targeting Android 6.0 (with Google APis) is not available, go ahead and download it.

Press Next to give your Virtual Device a name, and then click Finish.

You'll be returned to the list of Your Virtual Devices. On this, click the ‘Start' button beside your new device:

After a few moments, the emulator will open.

Readying the Emulator for Location Services.

Once the emulator launches, find the Google folder on its home screen.

Inside this folder, you'll find the Google Maps application. Launch it. You'll see a default map. At the bottom right of the map, you'll see the location button. Press this, and you'll be asked to give the phone permissions to use location.

Then, on the right hand side of the emulator, you'll see an action bar of emulator controls.

At the bottom of this bar, you'll see a caret ‘...' control. Select it. This will open the extended controls dialog, and the top item on it will be location, giving you a dialog like this:

On the right hand side of this, you can enter a Latitude and Longitude. Enter anything you like here, but make sure it's in an area with plenty of places. For example, use Latitude 49.2772 and Longitude -123.108 for downtown Vancouver in British Columbia, Canada to replicate the results from this lab.

Once you're done, press ‘Send', and the map will update with this location:

You're now ready to run the app and test it with location.

Running the App

From Android Studio, press the ‘Debug' button to start the app running.

You'll be asked to select your deployment target -- the running emulator should appear on this list. Select it, and Android Studio will deploy the app to the emulator for you.

After a few moments, the app will launch, and you'll see it with the single button and unpopulated list:

Press the ‘Get Current Place' button, and you'll see the permissions dialog. Click ‘Allow' to give the app permissions to location.

The App will now populate the list with named places close to the location. Close to this location in downtown Vancouver are places like BC Place or Andy Livingstone Park. Because the location wasn't clearly at any of these, they were a list of likely places you may be in!

To demonstrate this, on the emulator, switch to Google Maps. Then use the extended tools to change the location to Latitude: 49.27675 and Longitude: -123.114193. You'll see that the location changes.

Return to the app and press the ‘Get Current Place' button. It will give you a new list of places -- namely the stadium on the map, and a restaurant outside.

And that's it! You've now built a simple app that checks for the places at the current location, and gives you a likelihood of which ones you're at. Enjoy!