bgabriel998/SoftwareDevProject

View on GitHub
app/src/main/java/ch/epfl/sdp/peakar/points/GPSTracker.java

Summary

Maintainability
A
0 mins
Test Coverage
A
92%
package ch.epfl.sdp.peakar.points;

import android.Manifest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import androidx.core.app.ActivityCompat;

/**
 * GPSTracker is a class that provides update on the user location for the UserPoint class.
 * It is observed by userPoint.
 * It incapsulates the state information needed for retrieving the user location.
 * It extends Service and implements LocationListener, that allow for the UserPoint to get
 * notified and update when a  change in location is detected
 *
 * <ul>
 * <li> MIN_DISTANCE_CHANGE_FOR_UPDATES is the minimum delta in meters that can be detected
 * <li> MIN_TIME_BW_UPDATES is the minimum time in milliseconds to pass to request a new location
 * </ul>
 * <p>
 */
public class GPSTracker extends Service implements LocationListener {

    // fixed coordinates
    public static final double DEFAULT_LAT = 27.988056;
    public static final double DEFAULT_LON = 86.925278;
    public static final double DEFAULT_ALT = 8848.86;
    public static final double DEFAULT_ACC = 0.0;

    // current app context
    private final Context mContext;

    // flag for GPS status
    private boolean isGPSEnabled = false;

    // flag for network status
    private boolean isNetworkEnabled = false;

    // flag for GPS status
    private boolean canGetLocation = false;

    protected  Location location; // location

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 0; // 1 meter

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000; // 1 second

    // Declaring a Location Manager
    protected LocationManager locationManager;

    // User Point
    UserPoint userPoint;

    /**
     * Constructor for GPSTracker class
     *
     * @param mContext current context of the application
     * @param userPoint observer UserPoint
     */
    public GPSTracker(Context mContext, UserPoint userPoint) {
        this.mContext = mContext;
        this.userPoint = userPoint;
        getLocation();
    }


    /**
     * Method used to get updates on the location. Once called it will request a new location to the
     * location manager and update the location.
     * Before requesting the location the method will check if the requisites are satisfied, and
     * will request a new location via NETWORK_PROVIDER if it is available, otherwise it will request
     * it through the GPS_PROVIDER (less precise)
     *
     * In case it fails to do so it will stop the program and print the stack strace of the error
     */
    private void getLocation() {
        try {
            checkLocationManagerStatus();
            // First get location from Network Provider
            if (isNetworkEnabled) { setLocationProvider(LocationManager.NETWORK_PROVIDER, "Network"); }
            // Then set location from GPS Provider
            if (isGPSEnabled && location == null) { setLocationProvider(LocationManager.GPS_PROVIDER, "GPS"); }

            // handle case were no provider is enabled
            if (!isNetworkEnabled && !isGPSEnabled) { Log.d("No provider enabled", "Using default coordinates"); }

            //handle null location, set default location
            if (location == null) {
                setDefaultLocation();
                Log.d("Unable to retrieve location", "Using default coordinates");
            }

        } catch (Exception e) {
            Log.d("GPS ERROR", "read stack trace");
            e.printStackTrace();
        }

    }

    /**
     * Method that checks which providers are enabled for requesting location
     * It will update class variable isNetworkEnabled and isGPSEnabled accordingly
     */
    private void checkLocationManagerStatus() {
        locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);

        // getting network status
        isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        // getting GPS status
        isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    }

    /**
     * Sets the provider for the location manager
     *
     * @param locationManagerProvider   selected provider
     * @param logMessage                message to log (name of the provider)
     */
    private void setLocationProvider(String locationManagerProvider, String logMessage) {
        this.canGetLocation = true;
        setLocation(locationManagerProvider);
    }

    /**
     * This method handles the request to update the location.
     * First it checks if the app has the proper permissions, then it will request a new location
     * through the location manager
     *
     * @param selectedProvider  string indicating the chosen provider for requesting location
     */
    private void setLocation(String selectedProvider) {
        //check the permission
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            locationManager.requestLocationUpdates(
                    selectedProvider,
                    MIN_TIME_BW_UPDATES,
                    MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
            if (locationManager != null) {
                location = locationManager.getLastKnownLocation(selectedProvider);
            }
        }
    }

    /**
     * Set the default location
     */
    private void setDefaultLocation() {
        location = new Location("");
        location.setLatitude(DEFAULT_LAT);
        location.setLongitude(DEFAULT_LON);
        location.setAltitude(DEFAULT_ALT);
        location.setAccuracy((float)DEFAULT_ACC);
    }

    /**
     *
     * @return latitude (in degrees)
     */
    public double getLatitude(){
        return location != null ? location.getLatitude() : DEFAULT_LAT;
    }

    /**
     *
     * @return longitude (in degrees)
     */
    public double getLongitude(){
        return location != null ? location.getLongitude() : DEFAULT_LON;
    }

    /**
     *
     * @return altitude (in meters)
     */
    public double getAltitude(){
        return location != null ? location.getAltitude() : DEFAULT_ALT;
    }

    /**
     *
     * @return accuracy (in meters)
     */
    public double getAccuracy() {
        return location != null ? location.getAccuracy() : DEFAULT_ACC;
    }


    /**
     *
     * @return  <code>true</code> if is able to get the current location;
     *          <code>false</code> otherwise.
     */
    public boolean canGetLocation() {
        return this.canGetLocation;
    }

    /**
     * Method from interface LocationListener.
     * Called when the location has changed and locations are being delivered in batches.
     * Once called it will update the UserPoint that is observing this object.
     *
     * @param location current location
     */
    @Override
    public void onLocationChanged(Location location) {
        getLocation();
        userPoint.update();
    }

    /**
     * Method from interface LocationListener.
     * Called when the provider this listener is registered with becomes disabled.
     * Throws an error.
     *
     * @param provider current provider
     */
    @Override
    public void onProviderDisabled(String provider) {
        // print some error
    }

    /**
     * Method from interface LocationListener.
     * Called when a provider this listener is registered with becomes enabled.
     * Once called it will update the UserPoint that is observing this object.
     *
     * @param provider current provider
     */
    @Override
    public void onProviderEnabled(String provider) {
        getLocation();
        userPoint.update();
    }

    /**
     * Method from interface LocationListener.
     * This method was deprecated in API level 29. This callback will never be invoked on Android Q and above.
     *  Once called it will update the UserPoint that is observing this object.
     *
     * @param provider current provider
     * @param status current status
     * @param extras extras
     */
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        getLocation();
        userPoint.update();
    }

    /**
     * Method from class Service
     *
     * @param arg0 intent
     * @return the communication channel to the service.
     */
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

}