Assignment 12

Welcome to week ten of Mobile Systems and Applications! (cs.upt.ro/~alext/msa/lab12)

Topics of discussion

  • Integrating Google Maps in Android Apps

  • Drawing location markers

  • Setting listeners on the map

  • Computing distances

Google Maps

The subject of this lab is to integrate Google Maps in an Android application. To give you a taste of the basic functionalities made available by Google Maps, we will follow a set of simple sets, and the end results should be similar to what you see in Figure 1.

Fig.1. Calculated distance between two custom markers on a Google Map.

Creating an application with Google Maps

- Create a new project in Android Studio and choose as a main activity a Google Maps Activity. - Once you press finish, a file named google_maps_api.xml should open automatically, instructing you to get a Google Maps API key. The link should be similar to https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=87:C6:45:D6:DC:22:33:4F:1E:3A:41:23:29:7D:FC:49:34:B9:A1:AF%3Bcom.upt.cti.smartwallet.gmapstest and you can open it in your browser. Make sure you are logged in with your google account, then simply select the "create new project" option, agree to the terms and conditions (of course), and wait for the API to be enabled in your newly created project.

- After about 10-20 seconds, you should see that the API was enabled and you can further generate a key. The key should look like in Figure 2, and have to copy-paste it in your project's google_maps_api.xml file, like displayed in Figure 3.

- Finally, run the project on the emulator or on your mobile.

Fig. 2.

Fig. 3.

Now switch to the main activity's code. By default, it will initialize a Google Map inside a fragment, and will place a marker at coordinates (-34, 151), meaning 34 S latitude, and 151 E longitude, which is Sydney, Australia. The layout file of the main activity consists of a single fragment. A <fragment> tag allows a layout file to dynamically include other, unknown layouts at runtime. In this case, we are using a SupportMapFragment, which has to be found though its fully qualified name. The full path of the class points to the gms.maps package provided by Google through the Gradle dependency.

<fragment android:id="@+id/map"

android:name="com.google.android.gms.maps.SupportMapFragment"

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:map="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context="com.topindustries.testgooglemaps.MapsActivity"/>

Show the current location button

In order to switch from the hardcoded Sydney location, and show our current location button, the following line should be included in the onMapReady method: mMap.setMyLocationEnabled(true). For Android 6.0 and later the permissions are added on demand, thus the app has to request to obtain the permission in order this button to work. The following sequences of code should be used: In the onMapReady method:

if (checkPermission())

mMap.setMyLocationEnabled(true);

else

askPermission();

Inside the class add the following two helper methods:

private final int REQ_PERMISSION = 5;

// Check for permission to access Location

private boolean checkPermission() {

// Ask for permission if it wasn't granted yet

return (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)

== PackageManager.PERMISSION_GRANTED);

}

// Asks for permission

private void askPermission() {

ActivityCompat.requestPermissions(

this,

new String[]{Manifest.permission.ACCESS_FINE_LOCATION},

REQ_PERMISSION

);

}

And finally, add the callback handler for granted permissions:

@Override

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

super.onRequestPermissionsResult(requestCode, permissions, grantResults);

switch (requestCode) {

case REQ_PERMISSION: {

if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission granted

if (checkPermission())

mMap.setMyLocationEnabled(true);

} else {

// Permission denied

}

break;

}

}

}

Please note what happens when the button is pressed. For testing the button in emulator, click on the 3 points (Extended controls ...) and the latitude and longitude will be set. The button will be clicked in the screen with Google Maps.

Setting the map type

The Google Maps Android API offers 4+1 types of maps:

  • Normal - displays the default road map view. This is the default map type.

  • Satellite - displays Google Earth satellite images

  • Hybrid - displays a mixture of normal and satellite views

  • Terrain - displays a physical map based on terrain information

  • None - check it out to see what it does

To set up the type just call:

Fig. 4.

Centering the map on a location

Get any lat-long coordinates from https://maps.google.com, like for example the coordinates of your faculty.

Centering the map on the faculty location is done by calling:

LatLng facultate = new LatLng(45.7450284, 21.2275766);

mMap.moveCamera(CameraUpdateFactory.newLatLng(facultate));

mMap.animateCamera(CameraUpdateFactory.zoomTo(18));

Test 3 different values for the argument of the zoomTo call and select the most appropriate one.

Adding markers

A marker is placed on the faculty location using the previously created LatLng object, combined with a string title:

mMap.addMarker(new MarkerOptions()

.position(facultate)

.title("Facultate"));

Note what happened when the marker is clicked.

Add another marker for a destination, somewhere within the area.

Customizing markers

In order to customize markers, you need to add a library in the build gradle file: compile 'com.google.maps.android:android-maps-utils:0.4+' Now you are set, so you can specify details such as background color, text color and the text to be shown. The markers are customized as follows:

IconGenerator iconGenerator = new IconGenerator(this);

iconGenerator.setColor(ContextCompat.getColor(this, android.R.color.holo_blue_dark));

iconGenerator.setTextAppearance(R.color.textColor);

mMap.addMarker(new MarkerOptions()

.position(facultate)

.icon(BitmapDescriptorFactory.fromBitmap(iconGenerator.makeIcon("Facultate"))));

The text color is specified in colors.xml, similar to the existing three colors. Update: to apply the text appearance you'll have to define a new <style> in styles.xml. This is a more robust solution, as a style can define more than just the text color, namely font size, font family, styling on the font etc.

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

<resources>

<color name="colorPrimary">#3F51B5</color>

<color name="colorPrimaryDark">#303F9F</color>

<color name="colorAccent">#FF4081</color>

<color name="textColor">#ffffee</color>

</resources>

<style>

<item name="textSize">14sp</item>

<item name="textColor">#ffffff</item>

</style>

Go ahead and customize the colors in a different way each of the two markers.

Adding a polyline to the map

A polyline connects two or more lat-long objects on the map and is drawn by the following method, having as parameters the list of the markers coordinates and the map:

//Draw polyline on the map

public void drawPolyLineOnMap(List<LatLng> list, GoogleMap googleMap) {

PolylineOptions polyOptions = new PolylineOptions();

polyOptions.color(Color.GREEN);

polyOptions.width(8);

polyOptions.addAll(list);

googleMap.addPolyline(polyOptions);

LatLngBounds.Builder builder = new LatLngBounds.Builder();

for (LatLng latLng : list) {

builder.include(latLng);

}

builder.build();

}

Of course, you will have to call the method from onMapReady, and pass the two points on the map as a list.

Click event on markers

Finally, we want to be able to interact with the markers. To show a Toast at the bottom of the screen with the pressed location name, attach the following method:

mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {

@Override

public boolean onMarkerClick(Marker marker) {

if (marker.getPosition().equals(facultate)) {

Toast.makeText(MapsActivity.this, "I'm studying", Toast.LENGTH_LONG).show();

} else {

// ...

}

return false;

}

});

A marker is identified using its position.

Showing distance on destination marker click

To show the distance between any two markers on the map, in a straight line, use the SphericalUtil class as:

SphericalUtil.computeDistanceBetween(facultate, opera);

To get the route between two or more points you have to use the Google Maps API which can be queried like so:

Maps API query for route between Timisoara and Oradea

http://maps.googleapis.com/maps/api/directions/json?origin=Timisoara&destination=Oradea&sensor=false

And you will get a JSON with all checkpoints that can be parsed to a polyline, then placed on the map.

Extra reading

Documentation for Google Maps API

Have a fruitful week.

Oroblanco fruit

Oroblanco is a cross between an acidless pomelo and a white grapefruit. It's a sweeter fruit, large in size with fewer seeds. Oroblanco fruit taste is similar to the scent of its flowers. The trees of Oroblanco do not grow in cold conditions. It has a tendency to adapt to its environment very fast and it is a vigorous grower.

http://www.fruitsinfo.com/oroblanco-hybrid-fruits.php