diff --git a/app/src/main/java/com/a1/nextlocation/data/RouteHandler.java b/app/src/main/java/com/a1/nextlocation/data/RouteHandler.java new file mode 100644 index 0000000..0075475 --- /dev/null +++ b/app/src/main/java/com/a1/nextlocation/data/RouteHandler.java @@ -0,0 +1,69 @@ +package com.a1.nextlocation.data; + +import org.osmdroid.views.overlay.Polyline; + +/** + * singleton to track the current route that is being followed + */ +public enum RouteHandler { + INSTANCE; + + private boolean isFollowingRoute = false; + private Route currentRoute; + private int stepCount = 0; + private RouteFinishedListener routeFinishedListener; + + private Polyline currentRouteLine; + + public void setCurrentRouteLine(Polyline currentRouteLine) { + this.currentRouteLine = currentRouteLine; + } + + public Polyline getCurrentRouteLine() { + return currentRouteLine; + } + + public void setRouteFinishedListener(RouteFinishedListener routeFinishedListener) { + this.routeFinishedListener = routeFinishedListener; + } + + public int getStepCount() { + return stepCount; + } + + public void addStep() { + stepCount++; + } + + public void finishRoute() { + stepCount = 0; + isFollowingRoute = false; + currentRoute = null; + currentRouteLine = null; + } + + public void followRoute(Route route) { + this.currentRoute = route; + setFollowingRoute(true); + } + + public boolean isFollowingRoute(Route route) { + return isFollowingRoute && route.equals(currentRoute); + } + public void setFollowingRoute(boolean followingRoute) { + isFollowingRoute = followingRoute; + } + + public boolean isFollowingRoute() { + return isFollowingRoute; + } + + public Route getCurrentRoute() { + return currentRoute; + } + + @FunctionalInterface + public interface RouteFinishedListener { + void onRouteFinish(); + } +} diff --git a/app/src/main/java/com/a1/nextlocation/data/StaticData.java b/app/src/main/java/com/a1/nextlocation/data/StaticData.java index 0f0d4e3..d347d42 100644 --- a/app/src/main/java/com/a1/nextlocation/data/StaticData.java +++ b/app/src/main/java/com/a1/nextlocation/data/StaticData.java @@ -34,14 +34,6 @@ public enum StaticData { return locationsVisited; } - private Polyline currentRoute; - public void setCurrentRoute(Polyline currentRoute) { - this.currentRoute = currentRoute; - } - - public Polyline getCurrentRoute() { - return currentRoute; - } } diff --git a/app/src/main/java/com/a1/nextlocation/fragments/HomeFragment.java b/app/src/main/java/com/a1/nextlocation/fragments/HomeFragment.java index 9212d47..b5529ef 100644 --- a/app/src/main/java/com/a1/nextlocation/fragments/HomeFragment.java +++ b/app/src/main/java/com/a1/nextlocation/fragments/HomeFragment.java @@ -1,7 +1,6 @@ package com.a1.nextlocation.fragments; - import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; @@ -25,6 +24,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import com.a1.nextlocation.R; +import com.a1.nextlocation.data.RouteHandler; import com.a1.nextlocation.data.StaticData; import com.a1.nextlocation.json.DirectionsResult; import com.a1.nextlocation.network.ApiHandler; @@ -47,17 +47,19 @@ import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay; import java.util.ArrayList; import java.util.List; -public class HomeFragment extends Fragment implements LocationListener, DirectionsListener { +public class HomeFragment extends Fragment implements LocationListener { private final String userAgent = "com.ai.nextlocation.fragments"; public final static String MAPQUEST_API_KEY = "vuyXjqnAADpjeL9QwtgWGleIk95e36My"; private ImageButton imageButton; + private ImageButton stopButton; private MapView mapView; private final int REQUEST_PERMISSIONS_REQUEST_CODE = 1; private final String TAG = HomeFragment.class.getCanonicalName(); -// private RoadManager roadManager; + // private RoadManager roadManager; private Polyline roadOverlay; private int color; private Location currentLocation; + private Overlay allLocationsOverlay; @Override public void onCreate(Bundle savedInstanceState) { @@ -67,8 +69,6 @@ public class HomeFragment extends Fragment implements LocationListener, Directio Manifest.permission.ACCESS_FINE_LOCATION, // WRITE_EXTERNAL_STORAGE is required in order to show the map Manifest.permission.WRITE_EXTERNAL_STORAGE); -// roadManager = new MapQuestRoadManager(MAPQUEST_API_KEY); -// roadManager.addRequestOption("routeType=foot-walking"); color = requireContext().getColor(R.color.red); } @@ -79,33 +79,48 @@ public class HomeFragment extends Fragment implements LocationListener, Directio View view = inflater.inflate(R.layout.fragment_home, container, false); + // set up the location list button this.imageButton = view.findViewById(R.id.location_list_button); this.imageButton.setOnClickListener(v -> { LocationFragment locationFragment = new LocationFragment(); ((FragmentActivity) view.getContext()).getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, locationFragment).addToBackStack(null).commit(); }); - ApiHandler.INSTANCE.addListener(this); + // set up the route stop button + stopButton = view.findViewById(R.id.home_stop_route_button); + stopButton.setOnClickListener(v -> { + RouteHandler.INSTANCE.finishRoute(); + stopButton.setVisibility(View.GONE); + Toast.makeText(requireContext(),getResources().getString(R.string.route_stop_toast),Toast.LENGTH_SHORT).show(); + mapView.getOverlays().remove(roadOverlay); + mapView.getOverlays().remove(allLocationsOverlay); + addLocations(); + mapView.invalidate(); + roadOverlay = null; + }); + + if (RouteHandler.INSTANCE.isFollowingRoute()) { + stopButton.setVisibility(View.VISIBLE); + } else { + stopButton.setVisibility(View.GONE); + } + ApiHandler.INSTANCE.addListener(this::onDirectionsAvailable); return view; } - @Override - public void onDirectionsAvailable(DirectionsResult directionsResult) { + /** + * callback method that gets called when there are new directions available in the form of a {@link DirectionsResult} object. + * @param directionsResult the directions received from the api + */ + private void onDirectionsAvailable(DirectionsResult directionsResult) { Log.d(TAG, "onDirectionsAvailable: got result! " + directionsResult); ArrayList geoPoints = directionsResult.getGeoPoints(); roadOverlay = new Polyline(); roadOverlay.setPoints(geoPoints); - - // this is for mapquest, but it gives a "no value for guidancelinkcollection" error and google has never heard of that -// GeoPoint[] gp = directionsResult.getStartAndEndPoint(); -// ArrayList arrayList = new ArrayList<>(Arrays.asList(gp)); -// Road road = roadManager.getRoad(arrayList); -// roadOverlay = RoadManager.buildRoadOverlay(road); - roadOverlay.setColor(color); - StaticData.INSTANCE.setCurrentRoute(roadOverlay); + RouteHandler.INSTANCE.setCurrentRouteLine(roadOverlay); Log.d(TAG, "onDirectionsAvailable: successfully added road!"); } @@ -119,6 +134,7 @@ public class HomeFragment extends Fragment implements LocationListener, Directio /** * This method initializes the map and all the things it needs + * * @param view the view the map is on */ private void initMap(@NonNull View view) { @@ -135,7 +151,7 @@ public class HomeFragment extends Fragment implements LocationListener, Directio GpsMyLocationProvider gpsMyLocationProvider = new GpsMyLocationProvider(this.requireContext()); // add the compass overlay - CompassOverlay compassOverlay = new CompassOverlay(requireContext(),new InternalCompassOrientationProvider(requireContext()),mapView); + CompassOverlay compassOverlay = new CompassOverlay(requireContext(), new InternalCompassOrientationProvider(requireContext()), mapView); compassOverlay.enableCompass(); mapView.getOverlays().add(compassOverlay); @@ -155,19 +171,17 @@ public class HomeFragment extends Fragment implements LocationListener, Directio LocationManager locationManager = (LocationManager) requireActivity().getSystemService(Context.LOCATION_SERVICE); - - try { - locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,0,0,this); - locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,0,0,this); + // request location updates for the distance checking + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this); + locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); + // get the current location and set it as center Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); - if (currentLocation == null) { - currentLocation = location; - } + if (currentLocation == null) currentLocation = location; - if( location != null ) { + if (location != null) { GeoPoint start = new GeoPoint(location.getLatitude(), location.getLongitude()); mapController.setCenter(start); } @@ -188,59 +202,94 @@ public class HomeFragment extends Fragment implements LocationListener, Directio } + /** + * displays the route that is currently being followed as a red line + */ private void displayRoute() { - if (roadOverlay == null) { - if (StaticData.INSTANCE.getCurrentRoute() != null) { - roadOverlay = StaticData.INSTANCE.getCurrentRoute(); + if (RouteHandler.INSTANCE.isFollowingRoute()) { + if (roadOverlay == null) { + if (RouteHandler.INSTANCE.getCurrentRouteLine() != null) { + roadOverlay = RouteHandler.INSTANCE.getCurrentRouteLine(); + mapView.getOverlays().add(roadOverlay); + mapView.invalidate(); + Log.d(TAG, "initMap: successfully added road!"); + } + } else { mapView.getOverlays().add(roadOverlay); mapView.invalidate(); Log.d(TAG, "initMap: successfully added road!"); } - } else { - mapView.getOverlays().add(roadOverlay); - mapView.invalidate(); - Log.d(TAG, "initMap: successfully added road!"); } } + /** + * adds the locations of the current route to the map. If there is no current route, show all locations + */ private void addLocations() { - List locations = LocationListManager.INSTANCE.getLocationList(); + // get the locations of the current route or all locations + List locations = RouteHandler.INSTANCE.isFollowingRoute() ? RouteHandler.INSTANCE.getCurrentRoute().getLocations() : LocationListManager.INSTANCE.getLocationList(); final ArrayList items = new ArrayList<>(locations.size()); - Drawable marker = ContextCompat.getDrawable(requireContext(),R.drawable.ic_baseline_location_on_24); + // marker icon + Drawable marker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_baseline_location_on_24); marker.setAlpha(255); marker.setTint(getResources().getColor(R.color.primaryColour)); + + // add all locations to the overlay itemss for (com.a1.nextlocation.data.Location location : locations) { - OverlayItem item = new OverlayItem(location.getName(),location.getDescription(),location.convertToGeoPoint()); + OverlayItem item = new OverlayItem(location.getName(), location.getDescription(), location.convertToGeoPoint()); item.setMarker(marker); items.add(item); } - Overlay allLocationsOverlay = new ItemizedIconOverlay(items, + + // create the overlay that will hold all locations and add listeners + allLocationsOverlay = new ItemizedIconOverlay(items, new ItemizedIconOverlay.OnItemGestureListener() { + /** + * on sinlge click, navigate to that location's detail fragment + * @param index the index in the location list + * @param item the item that was clicked + * @return true + */ @Override public boolean onItemSingleTapUp(int index, OverlayItem item) { com.a1.nextlocation.data.Location clicked = locations.get(index); requireActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, new LocationDetailFragment(clicked)).commit(); - return false; + return true; } + /** + * on item long press, show that location's name in a toast message + * @param index the index in the location list + * @param item the item that was clicked + * @return true + */ @Override public boolean onItemLongPress(int index, OverlayItem item) { com.a1.nextlocation.data.Location clicked = locations.get(index); - Toast.makeText(requireContext(), clicked.getName(),Toast.LENGTH_SHORT).show(); + Toast.makeText(requireContext(), clicked.getName(), Toast.LENGTH_SHORT).show(); + + // create a route to the clicked location, didn't work and didn't have enough time to make it work ¯\_(ツ)_/¯ + // Route route = new Route("Route to " + clicked.getName()); // route.addLocation(new com.a1.nextlocation.data.Location("Current location",currentLocation.getLatitude(),currentLocation.getLongitude(),"your location",null)); // route.addLocation(clicked); // ApiHandler.INSTANCE.getDirections(route); return true; } - },requireContext()); + }, requireContext()); + // add the overlay to the map mapView.getOverlays().add(allLocationsOverlay); Log.d(TAG, "addLocations: successfully added locations"); } + /** + * @author Ricky + * request the permissions needed for location and network, made by Ricky + * @param permissions tbe permissions we want to ask + */ private void requestPermissionsIfNecessary(String... permissions) { ArrayList permissionsToRequest = new ArrayList<>(); if (this.getContext() != null) @@ -259,16 +308,21 @@ public class HomeFragment extends Fragment implements LocationListener, Directio } } + /** + * location callback that gets called each time the location is updated. It is used for updating the distance walked and checking if there are locations you have visited + * @param location the new location + */ @Override public void onLocationChanged(@NonNull Location location) { + // calculate the distance walked double distance = currentLocation.distanceTo(location); // in meters StaticData.INSTANCE.addDistance(distance); currentLocation = location; - //new thread because we don't want the main thread to hang + //new thread because we don't want the main thread to hang, this method gets called a lot Thread t = new Thread(() -> { for (com.a1.nextlocation.data.Location l : LocationListManager.INSTANCE.getLocationList()) { - if (com.a1.nextlocation.data.Location.getDistance(currentLocation.getLatitude(),currentLocation.getLongitude(),l.getLat(),l.getLong()) < 10) { + if (com.a1.nextlocation.data.Location.getDistance(currentLocation.getLatitude(), currentLocation.getLongitude(), l.getLat(), l.getLong()) < 10) { StaticData.INSTANCE.visitLocation(l); } } @@ -277,6 +331,8 @@ public class HomeFragment extends Fragment implements LocationListener, Directio t.start(); } + // empty override methods for the LocationListener + @Override public void onStatusChanged(String provider, int status, Bundle extras) { @@ -291,4 +347,22 @@ public class HomeFragment extends Fragment implements LocationListener, Directio public void onProviderDisabled(@NonNull String provider) { } + + /** + * method that gets called when the app gets paused + */ + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + /** + * method that gets called when the app gets resumed + */ + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } } \ No newline at end of file diff --git a/app/src/main/java/com/a1/nextlocation/fragments/RouteDetailFragment.java b/app/src/main/java/com/a1/nextlocation/fragments/RouteDetailFragment.java index 87eb2b1..4dfbfb9 100644 --- a/app/src/main/java/com/a1/nextlocation/fragments/RouteDetailFragment.java +++ b/app/src/main/java/com/a1/nextlocation/fragments/RouteDetailFragment.java @@ -15,6 +15,8 @@ import android.widget.Toast; import com.a1.nextlocation.R; import com.a1.nextlocation.data.Route; +import com.a1.nextlocation.data.RouteHandler; +import com.a1.nextlocation.data.StaticData; import com.a1.nextlocation.network.ApiHandler; public class RouteDetailFragment extends Fragment { @@ -53,6 +55,7 @@ public class RouteDetailFragment extends Fragment { public void startRoute(View view) { ApiHandler.INSTANCE.getDirections(route); + RouteHandler.INSTANCE.followRoute(route); Toast.makeText(requireContext(),"Route started!",Toast.LENGTH_SHORT).show(); ((FragmentActivity) view.getContext()).getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, new HomeFragment()).addToBackStack(null).commit(); diff --git a/app/src/main/java/com/a1/nextlocation/network/ApiHandler.java b/app/src/main/java/com/a1/nextlocation/network/ApiHandler.java index 00ff233..1a1cf03 100644 --- a/app/src/main/java/com/a1/nextlocation/network/ApiHandler.java +++ b/app/src/main/java/com/a1/nextlocation/network/ApiHandler.java @@ -127,6 +127,7 @@ public enum ApiHandler { } catch (IOException e) { Log.d(TAG, "getDirections: caught exception: " + e.getLocalizedMessage()); } + }); t.start(); diff --git a/app/src/main/res/drawable-anydpi/ic_stop_icon.xml b/app/src/main/res/drawable-anydpi/ic_stop_icon.xml new file mode 100644 index 0000000..addfeb0 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_stop_icon.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_stop_icon.png b/app/src/main/res/drawable-hdpi/ic_stop_icon.png new file mode 100644 index 0000000..10f865c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stop_icon.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_stop_icon.png b/app/src/main/res/drawable-mdpi/ic_stop_icon.png new file mode 100644 index 0000000..eec2a0b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stop_icon.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stop_icon.png b/app/src/main/res/drawable-xhdpi/ic_stop_icon.png new file mode 100644 index 0000000..e8ee9f5 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stop_icon.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stop_icon.png b/app/src/main/res/drawable-xxhdpi/ic_stop_icon.png new file mode 100644 index 0000000..b844819 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stop_icon.png differ diff --git a/app/src/main/res/drawable/stop_icon.png b/app/src/main/res/drawable/stop_icon.png new file mode 100644 index 0000000..7d72917 Binary files /dev/null and b/app/src/main/res/drawable/stop_icon.png differ diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 04ab7a7..c97c363 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -28,6 +28,17 @@ android:src="@drawable/ic_baseline_outlined_flag_24" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1cfcd8d..1e72755 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,4 +18,5 @@ activate Done cancel + Route stopped! \ No newline at end of file