/*
* Copyright (C) 2012- Peer internet solutions & Finalist IT Group
*
* This file is part of mixare.
*
* This program 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.
*
* This program 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
* this program. If not, see <http://www.gnu.org/licenses/>
*/
package org.mixare.mgr.location;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.mixare.MixContext;
import org.mixare.MixView;
import org.mixare.R;
import org.mixare.mgr.downloader.DownloadManager;
import android.content.Context;
import android.hardware.GeomagneticField;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.widget.Toast;
/**
* This class is repsonsible for finding the location, and sending it back to
* the mixcontext.
*
* @author A. Egal
*/
class LocationMgrImpl implements LocationFinder {
private LocationManager lm;
private String bestLocationProvider;
private final MixContext mixContext;
private Location curLoc;
private Location locationAtLastDownload;
private LocationFinderState state;
private final LocationObserver lob;
private List<LocationResolver> locationResolvers;
// frequency and minimum distance for update
// this values will only be used after there's a good GPS fix
// see back-off pattern discussion
// http://stackoverflow.com/questions/3433875/how-to-force-gps-provider-to-get-speed-in-android
// thanks Reto Meier for his presentation at gddde 2010
private final long freq = 5000; // 5 seconds
private final float dist = 20; // 20 meters
public LocationMgrImpl(MixContext mixContext) {
this.mixContext = mixContext;
this.lob = new LocationObserver(this);
this.state = LocationFinderState.Inactive;
this.locationResolvers = new ArrayList<LocationResolver>();
}
/*
* (non-Javadoc)
*
* @see
* org.mixare.mgr.location.LocationFinder#findLocation(android.content.Context
* )
*/
public void findLocation() {
// fallback for the case where GPS and network providers are disabled
Location hardFix = new Location("reverseGeocoded");
// Frangart, Eppan, Bozen, Italy
hardFix.setLatitude(46.480302);
hardFix.setLongitude(11.296005);
hardFix.setAltitude(300);
try {
requestBestLocationUpdates();
//temporary set the current location, until a good provider is found
curLoc = lm.getLastKnownLocation(lm.getBestProvider(new Criteria(), true));
} catch (Exception ex2) {
// ex2.printStackTrace();
curLoc = hardFix;
mixContext.doPopUp(R.string.connection_GPS_dialog_text);
}
}
private void requestBestLocationUpdates() {
Timer timer = new Timer();
for (String p : lm.getAllProviders()) {
if(lm.isProviderEnabled(p)){
LocationResolver lr = new LocationResolver(lm, p, this);
locationResolvers.add(lr);
lm.requestLocationUpdates(p, 0, 0, lr);
}
}
timer.schedule(new LocationTimerTask(),20* 1000); //wait 20 seconds for the location updates to find the location
}
/*
* (non-Javadoc)
*
* @see
* org.mixare.mgr.location.LocationFinder#locationCallback(android.content
* .Context)
*/
public void locationCallback(String provider) {
Location foundLocation = lm.getLastKnownLocation(provider);
if (bestLocationProvider != null) {
Location bestLocation = lm
.getLastKnownLocation(bestLocationProvider);
if (foundLocation.getAccuracy() < bestLocation.getAccuracy()) {
curLoc = foundLocation;
bestLocationProvider = provider;
}
} else {
curLoc = foundLocation;
bestLocationProvider = provider;
}
setLocationAtLastDownload(curLoc);
}
/*
* (non-Javadoc)
*
* @see org.mixare.mgr.location.LocationFinder#getCurrentLocation()
*/
public Location getCurrentLocation() {
if (curLoc == null) {
MixView mixView = mixContext.getActualMixView();
Toast.makeText(
mixView,
mixView.getResources().getString(
R.string.location_not_found), Toast.LENGTH_LONG)
.show();
throw new RuntimeException("No GPS Found");
}
synchronized (curLoc) {
return curLoc;
}
}
/*
* (non-Javadoc)
*
* @see org.mixare.mgr.location.LocationFinder#getLocationAtLastDownload()
*/
public Location getLocationAtLastDownload() {
return locationAtLastDownload;
}
/*
* (non-Javadoc)
*
* @see
* org.mixare.mgr.location.LocationFinder#setLocationAtLastDownload(android
* .location.Location)
*/
public void setLocationAtLastDownload(Location locationAtLastDownload) {
this.locationAtLastDownload = locationAtLastDownload;
}
/*
* (non-Javadoc)
*
* @see
* org.mixare.mgr.location.LocationFinder#setDownloadManager(org.mixare.
* mgr.downloader.DownloadManager)
*/
public void setDownloadManager(DownloadManager downloadManager) {
getObserver().setDownloadManager(downloadManager);
}
/*
* (non-Javadoc)
*
* @see org.mixare.mgr.location.LocationFinder#getGeomagneticField()
*/
public GeomagneticField getGeomagneticField() {
Location location = getCurrentLocation();
GeomagneticField gmf = new GeomagneticField(
(float) location.getLatitude(),
(float) location.getLongitude(),
(float) location.getAltitude(), System.currentTimeMillis());
return gmf;
}
public void setPosition(Location location) {
synchronized (curLoc) {
curLoc = location;
}
mixContext.getActualMixView().refresh();
Location lastLoc = getLocationAtLastDownload();
if (lastLoc == null) {
setLocationAtLastDownload(location);
}
}
@Override
public void switchOn() {
if (!LocationFinderState.Active.equals(state)) {
lm = (LocationManager) mixContext
.getSystemService(Context.LOCATION_SERVICE);
state = LocationFinderState.Confused;
}
}
@Override
public void switchOff() {
if (lm != null) {
lm.removeUpdates(getObserver());
state = LocationFinderState.Inactive;
}
}
@Override
public LocationFinderState getStatus() {
return state;
}
private synchronized LocationObserver getObserver() {
return lob;
}
class LocationTimerTask extends TimerTask {
@Override
public void run() {
//remove all location updates
for(LocationResolver locationResolver: locationResolvers){
lm.removeUpdates(locationResolver);
}
if(bestLocationProvider != null){
lm.removeUpdates(getObserver());
state=LocationFinderState.Confused;
mixContext.getActualMixView().runOnUiThread(new Runnable() {
@Override
public void run() {
lm.requestLocationUpdates(bestLocationProvider, freq, dist, getObserver());
}
});
state=LocationFinderState.Active;
}
else{ //no location found
mixContext.getActualMixView().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mixContext.getActualMixView(),
mixContext.getActualMixView().getResources().getString(
R.string.location_not_found), Toast.LENGTH_LONG);
}
});
}
}
}
}