/*
* Copyright (C) 2016-2107 Stichting Akvo (Akvo Foundation)
*
* This file is part of Akvo Flow.
*
* Akvo Flow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Akvo Flow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Akvo Flow. If not, see <http://www.gnu.org/licenses/>.
*/
package org.akvo.flow.event;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import java.util.Timer;
import java.util.TimerTask;
/**
* TimedLocationListener is a reusable helper class to get GPS locations.
* If geolocation is unknown after the LOCATION_TIMEOUT_IN_MS milliseconds, the caller will receive a
* time out event.
*/
public class TimedLocationListener implements LocationListener {
public static final float ACCURACY_DEFAULT = 20f; // 20 meters
private static final long LOCATION_TIMEOUT_IN_MS = 1000 * 60; // 1 minute
private static final float ACCURACY_UNRELIABLE = 0f;
private final Handler mHandler = new Handler();
private final Listener mListener;
private final LocationManager mLocationManager;
private final boolean mAllowMockupLocations;
private Timer mTimer;
private boolean mListeningLocation;
public TimedLocationListener(Context context, Listener listener, boolean allowMockupLocations) {
mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
mListener = listener;
mListeningLocation = false;
mAllowMockupLocations = allowMockupLocations;
}
public void start() {
if (!mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
mListener.onGPSDisabled();
return;
}
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
mListeningLocation = true;
// Ensure no pending tasks are running
if (mTimer != null) {
mTimer.cancel();
}
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
// Ensure it runs on the UI thread!
mHandler.post(new Runnable() {
@Override
public void run() {
if (mListeningLocation) {
stop();
mListener.onTimeout();
}
}
});
}
}, LOCATION_TIMEOUT_IN_MS);
}
public void stop() {
if (mTimer != null) {
mTimer.cancel();
}
mLocationManager.removeUpdates(this);
mListeningLocation = false;
}
public boolean isListening() {
return mListeningLocation;
}
@Override
public void onLocationChanged(Location location) {
if (isValid(location) && mListeningLocation) {
mListener.onLocationReady(location.getLatitude(), location.getLongitude(),
location.getAltitude(), location.getAccuracy());
}
}
public void onProviderDisabled(String provider) {
if (LocationManager.GPS_PROVIDER.equals(provider) && mListeningLocation) {
stop();// Cancel task and ensure state is updated before passing on the event
mListener.onGPSDisabled();
}
}
public void onProviderEnabled(String provider) {
}
public void onStatusChanged(String provider, int status, Bundle extras) {
}
private boolean isValid(Location location) {
return isLocationProviderValid(location) && isAccuracyValid(location);
}
private boolean isAccuracyValid(Location location) {
return location.getAccuracy() > ACCURACY_UNRELIABLE;
}
private boolean isLocationProviderValid(Location location) {
if (mAllowMockupLocations) {
return true;
}
return Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 || !location
.isFromMockProvider();
}
public interface Listener {
void onLocationReady(double latitude, double longitude, double altitude, float accuracy);
void onTimeout();
void onGPSDisabled();
}
}