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.
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().
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).
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.
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(); } } }