/*******************************************************************************
* Copyright (c) 2010 liw.
* All rights reserved.
*
* This file is part of VanBus.
*
* VanBus 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.
*
* VanBus 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 VanBus. If not, see <http://www.gnu.org/licenses/>.
* Contributors:
* liw - initial API and implementation
******************************************************************************/
package org.niclab.vanbus.gps;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.niclab.vanbus.application.VanBusApplication;
import android.app.Application;
import android.content.Context;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
public class MyLocationManager {
private static String LOG_TAG="MyLocationManager";
private Application appContext;
private Timer timer;
private LocationManager locationManager;
private LocationListener coarseLocationListener;
private LocationListener fineLocationListener;
private static final long coarseLocUpdateInterval = 500; //ms
private static final long fineLocUpdateInterval = 500; //ms
private static final long coarseLocUpdateDistance = 1000; //meters
private static final long fineLocUpdateDistance = 50; //meters
private static final int TWO_MINUTES = 1000 * 60 * 2;
private static final int HALF_A_MINUTE = 1000*30;
private Location currentBestLocation;
// boolean gps_enabled=false;
// boolean network_enabled=false;
public MyLocationManager(Application app){
appContext = app;
locationManager = (LocationManager) appContext.getSystemService(Context.LOCATION_SERVICE);
currentBestLocation = getLastKnownLocation();
}
public boolean isGPSEnabled(){
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
public boolean isNetworkEnabled(){
return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
}
public boolean isGPSandNetworkEnabled(){
return isGPSEnabled() && isNetworkEnabled();
}
public boolean isOnlyGPSEnabled(){
return isGPSEnabled() && !isNetworkEnabled();
}
public boolean isOnlyNetworkEnabled(){
return isNetworkEnabled() && !isGPSEnabled();
}
public boolean isGPSorNetworkEnabled(){
return isGPSEnabled() || isNetworkEnabled();
}
public void startPinDownMyLocation(){
//clear all the old listeners
if(coarseLocationListener !=null)
locationManager.removeUpdates(coarseLocationListener);
if(fineLocationListener !=null)
locationManager.removeUpdates(fineLocationListener);
// Initialize criteria for location providers
Criteria fine = new Criteria();
fine.setAccuracy(Criteria.ACCURACY_FINE);
Criteria coarse = new Criteria();
coarse.setAccuracy(Criteria.ACCURACY_COARSE);
creatLocationListeners();
// Will keep updating about every 500 ms until accuracy is about 1000 meters to get quick fix.
locationManager.requestLocationUpdates(locationManager.getBestProvider(coarse, true),0, 0, coarseLocationListener);
// Will keep updating about every 500 ms until accuracy is about 50 meters to get accurate fix.
locationManager.requestLocationUpdates(locationManager.getBestProvider(fine, true),0, 0, fineLocationListener);
}
private void creatLocationListeners(){
coarseLocationListener = new LocationListener(){
@Override
public void onLocationChanged(Location location) {
Log.v(LOG_TAG,"Location updated in coarseLocListener");
if(isBetterLocation(location, currentBestLocation))
currentBestLocation = location;
if (location.getAccuracy() < coarseLocUpdateDistance && location.hasAccuracy()){
locationManager.removeUpdates(this);
coarseLocationListener = null;
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status,
Bundle extras) {
}
};
fineLocationListener = new LocationListener(){
@Override
public void onLocationChanged(Location location) {
Log.v(LOG_TAG,"Location updated in fineLocListener");
if(isBetterLocation(location, currentBestLocation))
currentBestLocation = location;
if (location.getAccuracy() <fineLocUpdateDistance && location.hasAccuracy()){
locationManager.removeUpdates(this);
fineLocationListener = null;
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status,
Bundle extras) {
}
};
}
public Location getLastKnownLocation(){
if(isGPSEnabled())
return locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
else if(isNetworkEnabled())
return locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
else return null;
}
/**
* get current location. If ms >0, then this starts a new search location process and
* blocks the current thread for that long, then return a new location.
* If ms ==0, then return the current best location immediately.
*
* @param ms how long it waits to get the current location;
* @return the current best location
*/
public Location getCurrentBestLocation(long ms){
if(ms==0)
return currentBestLocation;
//if current best location is updated within 30seconds, we return it immediately.
if(currentBestLocation!=null){
if(System.currentTimeMillis()-currentBestLocation.getTime()<HALF_A_MINUTE)
return currentBestLocation;
}
//if the current listeners haven't been all removed, we do not start new listeners.
if(coarseLocationListener == null && fineLocationListener ==null)
this.startPinDownMyLocation();
VanBusApplication app = (VanBusApplication) appContext;
ScheduledFuture<Location> futureLoc= app.getScheduledExecutorService().schedule(new Callable<Location>(){
@Override
public Location call() throws Exception {
return currentBestLocation;
}
}, ms, TimeUnit.MILLISECONDS);
try {
return futureLoc.get();
} catch (Exception e) {
e.printStackTrace();
return currentBestLocation;
}
}
/** Determines whether one Location reading is better than the current Location fix
* @param location The new Location that you want to evaluate
* @param currentBestLocation The current Location fix, to which you want to compare the new one
*/
private boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
}
/*public class MyLocation {
Timer timer1;
LocationManager lm;
LocationResult locationResult;
boolean gps_enabled=false;
boolean network_enabled=false;
public boolean getLocation(Context context, LocationResult result)
{
//I use LocationResult callback class to pass location value from MyLocation to user code.
locationResult=result;
if(lm==null)
lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
//exceptions will be thrown if provider is not permitted.
try{gps_enabled=lm.isProviderEnabled(LocationManager.GPS_PROVIDER);}catch(Exception ex){}
try{network_enabled=lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);}catch(Exception ex){}
//don't start listeners if no provider is enabled
if(!gps_enabled && !network_enabled)
return false;
if(gps_enabled)
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
if(network_enabled)
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
timer1=new Timer();
timer1.schedule(new GetLastLocation(), 20000);
return true;
}
LocationListener locationListenerGps = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerNetwork);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
LocationListener locationListenerNetwork = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerGps);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
class GetLastLocation extends TimerTask {
@Override
public void run() {
lm.removeUpdates(locationListenerGps);
lm.removeUpdates(locationListenerNetwork);
Location net_loc=null, gps_loc=null;
if(gps_enabled)
gps_loc=lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(network_enabled)
net_loc=lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
//if there are both values use the latest one
if(gps_loc!=null && net_loc!=null){
if(gps_loc.getTime()>net_loc.getTime())
locationResult.gotLocation(gps_loc);
else
locationResult.gotLocation(net_loc);
return;
}
if(gps_loc!=null){
locationResult.gotLocation(gps_loc);
return;
}
if(net_loc!=null){
locationResult.gotLocation(net_loc);
return;
}
locationResult.gotLocation(null);
}
}
public static abstract class LocationResult{
public abstract void gotLocation(Location location);
}
}*/
|