WWDO - Jon H., Eric V., Mitchell W.

The basic structure of this write-up will be taking things in order as the user would experience it running the app and explaining how our code makes the thing happen.

The first thing the user experiences when they open the app is a message requesting to access the user's location. When the app is opened, a method called onResume() is run and in it, we check to see if the app has location permissions by calling the method hasLocationPermisions(). In hasLocationPermissions(), the app checks to see if it needs to ask the user for permissions and if yes, then it does so and returns false. Asking for permissions causes the onRequestPermissions() method to run and sets location permissions to true or false depending on the user's input. Otherwise it returns true and the onResume() method gets to continue on with its business.

Location Permissions (MapsActivity.java)

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
	private final int REQUEST_LOCATION_PERMISSIONS = 0;
	private static final in LOCATION_REQUEST = 500;
	private LocationRequest mLocationRequest;
	... 
	protected void onCreate(Bundle savedInstanceState) {
		... 
		mLocationRequest = new mLocationRequest;
		...
	}
	...
	@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
   switch (requestCode) {
       case LOCATION_REQUEST:
           if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
               mMap.setMyLocationEnabled(true);
           }
           break;
   }
}
@Override
public void onResume() {
   super.onResume();
   if (hasLocationPermission()) {
       mClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null);
   }
}
private boolean hasLocationPermission() {
   if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
       ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.ACCESS_FINE_LOCATION }, REQUEST_LOCATION_PERMISSIONS);
       return false;
   }
   return true;
}
	...
}

Once the user accepts the location permission request, the app extracts the user's location and Derek's hard coded location and draws a line between them. This event is kicked off inside of the onResume() method by taking the FusedLocationProviderClient() object class variable mClient and using it to call requestLocationUpdates(). This takes in mLocationCallback which calls updateMap().

Location Callback (MapsActivity.java)

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
	... 
	private FusedLocationProviderClient mClient;
private LocationRequest mLocationRequest;
private LocationCallback mLocationCallback;
	...
	protected void onCreate(Bundle savedInstanceState) {
		... 
		mLocationCallback = (LocationCallback) onLocationResult(locationResult) → {
			if (locationResult != null) {
				for (Location location : locationResult.getLocations()) {
					updateMap(location);
				}
			}
		};
		mClient = LocationServices.getFusedLocationProviderClient(this);
	}
	...
@Override
public void onResume() {
   super.onResume();
   if (hasLocationPermission()) {
       mClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null);
   }
}
	...
}

In updateMap(), we put our start and end points into an array and put markers down at each location. Next in updateMap(), we call getRequestUrl() which takes the two points we have and uses them to create and return a url. Once this url is returned from getRequestUrl() to updateMap(), the updateMap() method then uses the url in it's call to taskRequestDirections.execute(url).

Getting Request Url (MapsActivity.java)

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
	... 
	protected void onCreate(Bundle savedInstanceState) {
		... 
		mLocationCallback = (LocationCallback) onLocationResult(locationResult) → {
			if (locationResult != null) {
				for (Location location : locationResult.getLocations()) {
					updateMap(location);
				}
			}
		};
		...
	}
	...
private void updateMap(Location location){
   ...
   if(listPoints.size() >= 2) {
       String url = getRequestUrl(listPoints.get(0), listPoints.get(1));
       TaskRequestDirections taskRequestDirections = new TaskRequestDirections();
       taskRequestDirections.execute(url);
   }
}
	...
	private String getRequestUrl(LatLng origin, LatLng dest) {
   //Value of origin
   String str_origin = "origin=" + origin.latitude + "," + origin.longitude;
   //Value of destination
   String str_dest = "destination=" + dest.latitude + "," + dest.longitude;
   //Set value enable the sensor
   String sensor = "sensor=false";
   //Mode for find direction
   String mode;
   if (distance(origin.latitude, dest.latitude, origin.longitude, dest.longitude, 0, 0) > 1609) {
       mode = "mode=driving";
   }else {
       mode = "mode=walking";
   }
   //Build the full param
   String param = str_origin + "&" + str_dest + "&" + sensor + "&" + mode;
   //Output format
   String output = "json";
   //api key
   String apiKey = "&key=AIzaSyA2ge3xTYUPagFuMb5cWtR2Sk5aoNMDir0";
   //Create url to request
   String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + param  + apiKey;

   return url;
}
	...
}

TaskRequestDirections() is an Async class inside of the MapsActivity() class. It uses the request url to get json directions over the internet. In TaskRequestDirections() onPostExecute() method, which automatically runs when the object finishes, it takes the directions and calls taskParcer.execute() using the directions as a parament. TaskParcer() is another Async class inside of MapsActivity(). It takes the directions and calls DirectionsParser() to translate the json directions into information the app can use to make a line appear on the map.

Retrieving and parsing json directions (MapsActivity.java)

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
	... 
	public class TaskRequestDirections extends AsyncTask {
   @Override
   protected String doInBackground(String...strings) {
       String responseString = "";
       try {
           responseString = requestDirection(strings[0]);
       }catch (IOException e) {
           Log.d("exc", "exception caught");
           e.printStackTrace();
       }
       return responseString;
   }

   @Override
   protected void onPostExecute(String s) {
       super.onPostExecute(s);
       //Parse json here
       TaskParser taskParser = new TaskParser();
       taskParser.execute(s);
   }
}
public class TaskParser extends AsyncTask>>> {

   @Override
   protected List>> doInBackground(String...strings) {
       JSONObject jsonObject = null;
       List>> routes = null;
       try {
           jsonObject = new JSONObject(strings[0]);
           DirectionsParser directionsParser = new DirectionsParser();
           routes = directionsParser.parse(jsonObject);
       }catch (JSONException e) {
           e.printStackTrace();
       }
       return routes;
   }

   @Override
   protected void onPostExecute(List>> lists) {
       //Get list route and display it into the map
       ArrayList points = null;

       PolylineOptions polylineOptions = null;

       for(List> path : lists) {
           points = new ArrayList();
           polylineOptions = new PolylineOptions();

           for (HashMap point : path) {
               double lat = Double.parseDouble(point.get("lat"));
               double lng = Double.parseDouble(point.get("lng"));

               points.add(new LatLng(lat, lng));
           }

           polylineOptions.addAll(points);
           polylineOptions.width(15);
           polylineOptions.color(Color.BLUE);
           polylineOptions.geodesic(true);
       }

       if (polylineOptions != null) {
           mMap.addPolyline(polylineOptions);
       }else {
           Log.d("hey", "is this on");
           Toast.makeText(getApplicationContext(), "Direction not found", Toast.LENGTH_SHORT).show();
       }
   }
}

Significant Sources:

YouTube video that served as the starting skeleton to the final product:
Link - https://www.youtube.com/watch?v=jg1urt3FGCY&t=1284s

The DirectionsParser() class used in the video:
Created by - NgocTri on 12/11/2017.
Link - https://drive.google.com/file/d/1HWBIcBywRnqqSx7CvD9eF8iqg4Pq4tFV/view

The decodePolyline() method used in the DirectionsParser() class:
Link - http://jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java


Link to MapsActivity() code: http://cs.bemidjistate.edu/po4965wu/WWDO.txt
Link to DirectionsParser() code: http://cs.bemidjistate.edu/po4965wu/WWDODP.txt