CS5530 Mobile/Wireless Systems Using Map in Android

Yanyan Zhuang Department of Computer Science http://www.cs.uccs.edu/~yzhuang

CS5530 UC. Colorado Springs Ref. CN5E, NT@UW, WUSTL Setup • Install the services SDK

o Tools > Android > SDK manager

CS5530 2 Ref. CN5E, NT@UW, WUSTL Create a project • Select Google Maps Activity in the 'Add an activity to Mobile' dialog

• When build finished, Android Studio opens google_maps_api. and MapsActivity.

o google_maps_api.xml contains instructions on getting a Google Maps API key before you try to run the application.

CS5530 3 Ref. CN5E, NT@UW, WUSTL Get a Google Maps API key

• Apps need an API key to access Google Maps servers

o The key is free. You can use it with any of your applications that call the Google Maps Android API, and it supports an unlimited number of users

1. Copy the link in google_maps_api.xml, paste it into browser 2. Follow the instructions to create a new project on the Google API Console or select an existing project 3. Create an Android-restricted API key for your project 4. Copy the resulting API key, go back to Android Studio, and paste the API key into the element in google_maps_api.xml May use the same key for more than one app

CS5530 4 Ref. CN5E, NT@UW, WUSTL Run the app

• By default, the XML file that defines the app's layout is at res/layout/activity_maps.xml • The simplest way to see your app in action is to connect an Android device to your computer • You can use Android Emulator to run app

o When choosing an emulator, use Android 4.2.2 or higher, and be careful to pick an image that includes the Google , or the application will not have the runtime APIs in order to execute

CS5530 5 Ref. CN5E, NT@UW, WUSTL Run the app (2)

• You should see a map with a marker positioned over Sydney, Australia

o If you don't see a map, check that you've added an API key as described

o Check the log in Android Studio's Android Monitor for error about the API key

o Different ways to get the API key https://developers.google.com/maps/documentation/android- /signup

CS5530 6 Ref. CN5E, NT@UW, WUSTL the code

• AndroidManifest.xml

o (generated for you) • activity_maps.xml

o element to your activity's layout file

o Defines a SupportMapFragment to act as a container for the map and to provide access to the GoogleMap object

CS5530 7 Ref. CN5E, NT@UW, WUSTL Understand the code

• What is a fragment?

o A Fragment represents a behavior or a portion of in an Activity

o Can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities

o Can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running

} sort of like a "sub activity" that you can reuse in different activities

CS5530 8 Ref. CN5E, NT@UW, WUSTL Understand the code

• MapsActivity’s onCreate()

o @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // set the layout file as the content view setContentView(.layout.activity_maps); // get a handle to the map fragment SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); // use getMapAsync() to register for map callback (when map is ready) mapFragment.getMapAsync(this); }

CS5530 9 Ref. CN5E, NT@UW, WUSTL Understand the code

• Implement OnMapReadyCallback interface & override onMapReady(): set up the map when GoogleMap object is available

o public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback { // OnCreate() method here: as described on last slide @Override public void onMapReady(GoogleMap googleMap) { // Add a marker in Sydney, Australia, and move map's camera LatLng sydney = new LatLng(-33.852, 151.211); googleMap.addMarker(new MarkerOptions().position(sydney) .title("Marker in Sydney")); googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney)); } }

CS5530 10 Ref. CN5E, NT@UW, WUSTL Understand the code

• By default, the Google Maps API displays the content of “title” when the user taps a marker

o googleMap.addMarker(new MarkerOptions().position(sydney) .title("Marker in Sydney")); • There's no need to add a click listener for the marker if you’re happy with the default behavior

CS5530 11 Ref. CN5E, NT@UW, WUSTL Summarize

• The steps for adding a map

1. (You only need to do this once) Get Google Play Service SDK, obtain a key and add the required attributes to Android manifest

2. Add a element to the layout file for the Activity

3. Call getMapAsync() on the fragment to register the callback

4. Implement the OnMapReadyCallback interface and use the onMapReady(GoogleMap) callback to get a handle to the GoogleMap object

CS5530 12 Ref. CN5E, NT@UW, WUSTL Map Types • Normal • Hybrid • Terrain • Satellite • None

o GoogleMap map; ... // Sets the map type to be "hybrid" map.setMapType(GoogleMap.MAP_TYPE_HYBRID);

CS5530 13 Ref. CN5E, NT@UW, WUSTL Markers • You can customize markers by changing the default color, or replacing the marker icon with a custom image

o Info windows can provide additional context to a marker • Markers are objects of type Marker

o Added to map with the GoogleMap.addMarker(markerOptions) method

o Receive click events by default, and are often used with event listeners to bring up info windows

o Previous example } googleMap.addMarker(new MarkerOptions().position(sydney).title("Marker in Sydney"));

CS5530 14 Ref. CN5E, NT@UW, WUSTL Markers

• Some other examples

o Marker marker = googleMap.addMarker(new MarkerOptions().position(new LatLng(-34, 151)));

o marker.revmote();

o Setting a marker to be invisible, and transparency Marker marker = googleMap.addMarker(new MarkerOptions().position(new LatLng(-34, 151)).visible(false).alpha(0.8f));

CS5530 15 Ref. CN5E, NT@UW, WUSTL Markers

• Once a marker is created, you can change its property

o marker.setAlpha(1.0f);

o marker.setVisible(true);

o marker.setPosition(new LatLng(-34, 151)); • Why marker visibility?

o Faster to make an invisible marker visible, than creating a new marker • Show information

o googleMap.addMarker(new MarkerOptions().position(sydney).title("Sydney").snippet(“Population: 4.5 million in 2015”));

CS5530 16 Ref. CN5E, NT@UW, WUSTL Markers

• Respond to click events

o Activity must implement GoogleMap.OnMarkerClickListener

o Set a listener for marker click

} @Override public void onMapReady(GoogleMap googleMap) { …... mMap.setOnMarkerClickListener(this); }

o Implement callback function onMarkerClick()

CS5530 17 Ref. CN5E, NT@UW, WUSTL Markers

• Respond to click events

o Implement callback function onMarkerClick()

} @Override public boolean onMarkerClick(final Marker marker) { if (marker.equals(myMarker)) { Toast.makeText(this, marker.getTitle() + " has been clicked!", Toast.LENGTH_LONG).show(); } return false; }

CS5530 18 Ref. CN5E, NT@UW, WUSTL Toasts

• A toast provides simple feedback about an operation in a small pop-up

o Only fills the amount of space required for the message and the current activity remains visible and interactive

o Toasts automatically disappear after a timeout • Instantiate a Toast object with makeText() method

o 3 parameters: the application Context (usually this), the text message, and the duration for the toast

o Toast.LENGTH_SHORT and Toast.LENGTH_LONG

CS5530 19 Ref. CN5E, NT@UW, WUSTL Toasts

• Example

o Context context = getApplicationContext(); CharSequence text = "Hello toast!"; int duration = Toast.LENGTH_SHORT;

Toast toast = Toast.makeText(context, text, duration); toast.show();

o Toast.makeText(context, text, duration).show();

CS5530 20 Ref. CN5E, NT@UW, WUSTL Toasts

• Positioning your Toast

o A standard toast notification appears near the bottom of the screen, centered horizontally

o You can change this position with the setGravity(int, int, int) method: a Gravity constant, an x-position offset, and a y-position offset

o If the toast should appear in the top-left corner

} toast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0);

} If you want to nudge the position to the right, increase the value of the second parameter. To nudge it down, increase the value of the last parameter

CS5530 21 Ref. CN5E, NT@UW, WUSTL Select Current Place

• Create a Google API client

o Also known as the Google API client: connect with fused location provider and Google Places API

} private GoogleApiClient mGoogleApiClient;

CS5530 22 Ref. CN5E, NT@UW, WUSTL Select Current Place • Build a Google API client

o Request the fused location provider (LocationServices.API) and the two parts of the Google Places API for Android (Places.GEO_DATA_API and Places.PLACE_DETECTION_API)

o mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addConnectionCallbacks(this) .addApi(LocationServices.API) .addApi(Places.GEO_DATA_API) .addApi(Places.PLACE_DETECTION_API) .build(); mGoogleApiClient.connect();

CS5530 23 Ref. CN5E, NT@UW, WUSTL Select Current Place

• Cannot resolve LocationServices and Places • Add dependencies in build. (Module: app)

o compile 'com.google.android.gms:play-services-location:10.2.0' compile 'com.google.android.gms:play-services-places:10.2.0’

o Sync

o Then we can import classes for LocationServices and Places

CS5530 24 Ref. CN5E, NT@UW, WUSTL Select Current Place

• Call back functions of mGoogleApiClient.connect()

o @Override public void onConnected(Bundle connectionHint) { ... }

/* Handles failure to connect to the client. */ @Override public void onConnectionFailed(@NonNull ConnectionResult result) { Log.d(TAG, "Play services connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode()); }

/* Handles suspension of the connection to the Google Play services client. */ @Override public void onConnectionSuspended(int cause) { Log.d(TAG, "Play services connection suspended"); }

CS5530 25 Ref. CN5E, NT@UW, WUSTL Location Services API

• Enables or disables the my-location layer

o mMap.setMyLocationEnabled(true);

o While enabled and the location is available, the my-location layer continuously draws an indication of a user's current location and bearing, and displays UI controls that allow a user to interact with their location

o In order to use the my-location-layer feature you need to request permission for either ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION

CS5530 26 Ref. CN5E, NT@UW, WUSTL Location Services API

• Enables or disables the my-location button

o mMap.getUiSettings().setMyLocationButtonEnabled(true)

o The my-location button causes the camera to move such that the user's location is in the center of the map. If the button is enabled, it is only shown when the my-location layer is enabled

o By default, the my-location button is enabled

CS5530 27 Ref. CN5E, NT@UW, WUSTL Location Services API • Get the best and most recent location of the device (may be null in cases when a location is not available)

o mLastKnownLocation = LocationServices.FusedLocationApi .getLastLocation(mGoogleApiClient); • Set the map's camera position to the current location of the device and zoom in

o if (mLastKnownLocation != null) { mMap.moveCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(mLastKnownLocation.getLatitude(), mLastKnownLocation.getLongitude()), DEFAULT_ZOOM)); }

CS5530 28 Ref. CN5E, NT@UW, WUSTL Place Detection API

• Get an estimate of the place where the device is currently located

o PendingResult result = Places.PlaceDetectionApi .getCurrentPlace(mGoogleApiClient, null);

o Generates a PlaceLikelihoodBuffer based on device's last estimated location

o Returned values may be obtained by means of a network lookup: results are a best guess and not guaranteed to be correct

o Requires ACCESS_FINE_LOCATION permission

o Access to this method is subject to quota restrictions

CS5530 29 Ref. CN5E, NT@UW, WUSTL Place Detection API • Populate place information

o result.setResultCallback(new ResultCallback() { @Override public void onResult(@NonNull PlaceLikelihoodBuffer likelyPlaces) { mLikelyPlaceNames = new String[mMaxEntries]; … for (PlaceLikelihood placeLikelihood : likelyPlaces) { // Build a list of likely places to show the user. Max 5. mLikelyPlaceNames[i] = (String) placeLikelihood.getPlace().getName(); … } // Release the place likelihood buffer, to avoid memory leaks. likelyPlaces.release(); } });

CS5530 30 Ref. CN5E, NT@UW, WUSTL Setting an AlertDialog for Users to Pick

• AlertDialog lets users to interact with the app

o AlertDialog dialog = new AlertDialog.Builder(this) .setTitle("pick a place") .setItems(mLikelyPlaceNames, listener) .show();

o Syntax: use AlertDialog.Builder’s method setItems (CharSequence[] items, DialogInterface.OnClickListener listener)

} Set a list of items to be displayed in the dialog as the content

} Code will be notified of the selected item via the supplied listener

} Implement the listener

CS5530 31 Ref. CN5E, NT@UW, WUSTL Setting an AlertDialog for Users to Pick

• Listener

o DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // The "which" argument contains the position of the selected item. LatLng markerLatLng = mLikelyPlaceLatLngs[which]; String markerSnippet = mLikelyPlaceAddresses[which]; ... // Add a marker for the selected place, with an info window w/ information about that place mMap.addMarker(new MarkerOptions() .title(mLikelyPlaceNames[which]) .position(markerLatLng) .snippet(markerSnippet));

// Position the map's camera at the location of the marker. mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(markerLatLng, DEFAULT_ZOOM)); } };

CS5530 32 Ref. CN5E, NT@UW, WUSTL