package net.armooo.locationlog; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import android.content.ContextWrapper; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.os.Bundle; import android.util.Log; public class BestLocationProxy { private static final String TAG = "BestLocationProxy"; private LocationManager location_mgr; private List<ListenerRequest> requests; private boolean listening = false; private List<Listener> real_listeners; private Location last_known_location; private String best_provider; private List<String> provider_priority; private Map<String, ProviderStatus> provider_status; public BestLocationProxy(ContextWrapper context_wrapper) { location_mgr = (LocationManager) context_wrapper .getSystemService(ContextWrapper.LOCATION_SERVICE); requests = new ArrayList<ListenerRequest>(); provider_status = new HashMap<String, ProviderStatus>(); real_listeners = new ArrayList<Listener>(); provider_priority = new ArrayList<String>(); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); provider_priority.addAll(location_mgr.getProviders(criteria, false)); criteria.setAccuracy(Criteria.ACCURACY_COARSE); for (String provider : location_mgr.getProviders(criteria, false)) { if (!provider_priority.contains(provider)) { provider_priority.add(provider); } } for (String provider : location_mgr.getProviders(false)) { provider_status.put(provider, new ProviderStatus()); } change_best_provider(); } public Location getLastKnownLocation() { if (best_provider == null){ return null; } if (last_known_location == null) { return location_mgr.getLastKnownLocation(best_provider); } else { return last_known_location; } } public void requestLocationUpdates(long minTime, float minDistance, LocationListener listener) { ListenerRequest request = new ListenerRequest(minTime, minDistance, listener); requests.add(request); updated_requests(); } public void removeUpdates(LocationListener listener) { Iterator<ListenerRequest> itr = requests.iterator(); while (itr.hasNext()) { if (itr.next().getListener() == listener) { itr.remove(); updated_requests(); break; } } } private void updated_requests() { if (requests.isEmpty()) { for (LocationListener listener : real_listeners) { location_mgr.removeUpdates(listener); } listening = false; return; } long min_time = 0; float min_distance = 0; for (ListenerRequest request : requests) { if (request.getMinTime() < min_time) { min_time = request.getMinTime(); } if (request.getMinDistance() < min_distance) { min_distance = request.getMinDistance(); } } if (listening) { for (LocationListener listener : real_listeners) { location_mgr.removeUpdates(listener); } } for (String provider : location_mgr.getProviders(false)) { Listener listener = new Listener(); real_listeners.add(listener); location_mgr.requestLocationUpdates(provider, min_time, min_distance, listener); } listening = true; } private void change_best_provider() { Log.d(TAG, "Looking for best provider"); String new_provider = null; for (String provider : provider_priority) { ProviderStatus pstatus = provider_status.get(provider); int enabled = pstatus.getEnabled(); int status = pstatus.getStatus(); if (enabled == ProviderStatus.UNKNOWN) { if (location_mgr.isProviderEnabled(provider)) { enabled = ProviderStatus.ENABLED; } pstatus.setEnabled(enabled); } if (status == ProviderStatus.UNKNOWN) { Log.d(TAG, "Provider status is unknown " + provider); } if (enabled == ProviderStatus.DISABLED) { Log.d(TAG, "Provider is disabled " + provider); continue; } if (status != ProviderStatus.AVAILABLE) { Log.d(TAG, "Provider is not available " + provider); continue; } Log.d(TAG, "Using provider " + provider); new_provider = provider; break; } if (new_provider == null){ Criteria criteria = new Criteria(); new_provider = location_mgr.getBestProvider(criteria, true); if (new_provider == null){ Log.d(TAG, "No providers"); return; } Log.d(TAG, "Faling back to provider " + new_provider); } best_provider = new_provider; Location location = location_mgr.getLastKnownLocation(best_provider); if (location != null){ notifyLocationChanged(location); } } private void notifyLocationChanged(Location location){ Log.d(TAG, "Notifing change from " + location.getProvider()); last_known_location = location; for (ListenerRequest request : requests) { request.getListener().onLocationChanged(location); } } private class ListenerRequest { private long minTime; float minDistance; LocationListener listener; public ListenerRequest(long minTime, float minDistance, LocationListener listener) { this.minTime = minTime; this.minDistance = minDistance; this.listener = listener; } public long getMinTime() { return minTime; } public float getMinDistance() { return minDistance; } public LocationListener getListener() { return listener; } } private class ProviderStatus { public static final int UNKNOWN = -1; public static final int ENABLED = 2; public static final int DISABLED = 3; public static final int AVAILABLE = LocationProvider.AVAILABLE; public static final int OUT_OF_SERVICE = LocationProvider.OUT_OF_SERVICE; public static final int TEMPORARILY_UNAVAILABLE = LocationProvider.TEMPORARILY_UNAVAILABLE; private int enabled = UNKNOWN; private int status = UNKNOWN; public int getEnabled() { return enabled; } public void setEnabled(int enabled) { this.enabled = enabled; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } } private class Listener implements LocationListener { @Override public void onLocationChanged(Location location) { ProviderStatus status = provider_status.get(location.getProvider()); if (status.getStatus() == ProviderStatus.UNKNOWN){ status.setStatus(ProviderStatus.AVAILABLE); change_best_provider(); } if (!location.getProvider().equals(best_provider)) { Log.d(TAG, "Discarded change from " + location.getProvider()); return; } notifyLocationChanged(location); } @Override public void onProviderDisabled(String provider) { Log.d(TAG, "Provider disabled " + provider); provider_status.get(provider).setEnabled(ProviderStatus.DISABLED); change_best_provider(); } @Override public void onProviderEnabled(String provider) { Log.d(TAG, "Provider enabled " + provider); provider_status.get(provider).setEnabled(ProviderStatus.ENABLED); change_best_provider(); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.d(TAG, "Provider status changed " + provider + " " + Integer.toString(status)); provider_status.get(provider).setStatus(status); change_best_provider(); } } }