Welcome, Guest User :: Click here to login

Logo 67443

Lab 4A: Find My Car (Android)

Due Date: November 23

Objectives

  • Teach students how to find and work with locations in Android
  • Teach students how to create maps and drop pins
  • Reinforce previous mobile application development lessons

README.md

FindMyCar


This week, we will make an app that allows users to track where their car is parked and where they are relative to the car. We will be using Google Maps, Location, and we will save car data. Here is a sneak peak of the final app, but let's dive in!

Part 1: Google Maps

  1. Create a new project by going to File > New > New Project > Google Maps Activity with Kotlin and a Name of FindMyCar. Then select Finish.

  2. The first thing you need to do when working with locations to create an Android developer account. Visit https://developers.google.com/maps/documentation/android-sdk/start for instructions on how to get a Google Maps API key. Unfortunately, you cannot use your CMU address as a developer account so either use your personal Google account or create a new one just for this purpose. I recommend the latter.

  3. Once you have an API key add it to google_maps_api.xml file. When using Google Maps this requires having Google Play Services (also known as Google apps) installed within the virtual machine. Make sure you are setup to use ``Android 4.4 (Google APIs)’’ in Android Virtual Device Manager when selecting a target device.

  4. Go to MapsActivity.kt and replace the example Sydney code with the following

val pittsburgh = LatLng(40.4406, -79.9959)
mMap.addMarker(MarkerOptions().position(pittsburgh).title("Marker in Pittsburgh"))
mMap.moveCamera(CameraUpdateFactory.newLatLng(pittsburgh))
  1. Add the current location
    Visit https://developers.google.com/maps/documentation/android-sdk/current-place-tutorial for help.

Add the following snippets of code

    private fun getLocationPermission() {
        if (ContextCompat.checkSelfPermission(this.applicationContext, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            locationPermissionGranted = true
            mMap.isMyLocationEnabled = true
            mMap.uiSettings?.isMyLocationButtonEnabled = true
        } else {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                    PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION)
        }
    }
    companion object {
        private const val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1
        private val TAG = MapsActivity::class.java.simpleName
    }

and

private lateinit var mMap: GoogleMap
private val pittsburgh = LatLng(40.4406, -79.9959)
private val defaultLocation = pittsburgh
private var locationPermissionGranted = false
private val defaultZoom = 15.0F

Then call getLocationPermission() after initializing mMap in onMapReady()

Run your program just to make sure Android has no complaints.

Now add the following

private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
private var lastKnownLocation: Location? = null

As well as

private fun getDeviceLocation() {
        try {
            if (locationPermissionGranted) {
                val locationResult = fusedLocationProviderClient.lastLocation
                locationResult.addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        // Set the map's camera position to the current location of the device.
                        lastKnownLocation = task.result
                        if (lastKnownLocation != null) {
                            mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
                                LatLng(lastKnownLocation!!.latitude,
                                    lastKnownLocation!!.longitude), defaultZoom))
                        }
                    } else {
                        Log.d(TAG, "Current location is null. Using defaults.")
                        Log.e(TAG, "Exception: %s", task.exception)
                        mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(defaultLocation, defaultZoom))
                        mMap.uiSettings?.isMyLocationButtonEnabled = false
                    }
                }
            }
        } catch (e: SecurityException) {
            Log.e("Exception: %s", e.message, e)
        }
    }

Then add the following segment after your permission call

fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
getDeviceLocation()

Now your program should be able to find your location. When you run your program use the additional settings in the emulator to specify a current location (such as CMU) and play around with changing locations which will update automatically. Whenever you load the program the map will animate and zoom into your location. Try it. Set yourself at 10 Downing Street or 400 Pennsylvania Ave before loading the map and it will zoom to you.

Part 2: Adding Markers

  1. Add the following code to the end of onMapReady()
        mMap.setOnMapClickListener { point ->
            val marker = MarkerOptions().position(LatLng(point.latitude, point.longitude))
                .title("My Car")
            googleMap.addMarker(marker)
        }

Test it to see how it works. Of course this behavior is not quite what we want. Alter the program to respond only when you tap on your own location and then to remove the marker if you tap on yourself and you are close enough to the marker.

  1. Add a button called Where’s My Car that when tapped gives you car’s GPS coordinates but only when your car marker is set. Otherwise provide a fitting response (do not say "Dude Where’s My Car?’’).

Part 3: Saving State

Saving State is such a common feature in mobile apps that Android builds in a function called onSaveInstanceState(). Check out Googler Lyla Fujiwara’s description on how this behavior and then use it to save the instance of the car marker.