package com.mcxiaoke.next.geo; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import com.mcxiaoke.next.core.BuildConfig; import java.util.List; /** * Optimized implementation of Last Location Finder for devices running Gingerbread * and above. * <p/> * This class let's you find the "best" (most accurate and timely) previously * detected location using whatever providers are available. * <p/> * Where a timely / accurate previous location is not detected it will * return the newest location (where one exists) and setup a oneshot * location update to find the current location. */ @SuppressWarnings("ResourceType") public class LastLocationFinder { protected static String TAG = LastLocationFinder.class.getSimpleName(); protected static String SINGLE_LOCATION_UPDATE_ACTION = BuildConfig.APPLICATION_ID + ".action.SINGLE_LOCATION_UPDATE_ACTION"; protected PendingIntent pendingIntent; protected LocationListener locationListener; protected LocationManager locationManager; protected Context context; protected Criteria criteria; /** * Construct a new Gingerbread Last Location Finder. * * @param context Context */ public LastLocationFinder(Context context) { this.context = context; locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); // Coarse accuracy is specified here to get the fastest possible result. // The calling Activity will likely (or have already) request ongoing // updates using the Fine location provider. criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_LOW); // Construct the Pending Intent that will be broadcast by the oneshot // location update. Intent updateIntent = new Intent(SINGLE_LOCATION_UPDATE_ACTION); pendingIntent = PendingIntent.getBroadcast(context, 0, updateIntent, PendingIntent.FLAG_UPDATE_CURRENT); } /** * Returns the most accurate and timely previously detected location. * Where the last result is beyond the specified maximum distance or * latency a one-off location update is returned via the {@link LocationListener} * * @param minDistance Minimum distance before we require a location update. * @param minTime Minimum time required between location updates. * @return The most accurate and / or timely previously detected location. */ public Location getLastBestLocation(int minDistance, long minTime) { Location bestResult = null; float bestAccuracy = Float.MAX_VALUE; long bestTime = Long.MIN_VALUE; // Iterate through all the providers on the system, keeping // note of the most accurate result within the acceptable time limit. // If no result is found within maxTime, return the newest Location. List<String> matchingProviders = locationManager.getAllProviders(); for (String provider : matchingProviders) { Location location = locationManager.getLastKnownLocation(provider); if (location != null) { float accuracy = location.getAccuracy(); long time = location.getTime(); if ((time > minTime && accuracy < bestAccuracy)) { bestResult = location; bestAccuracy = accuracy; bestTime = time; } else if (time < minTime && bestAccuracy == Float.MAX_VALUE && time > bestTime) { bestResult = location; bestTime = time; } } } // If the best result is beyond the allowed time limit, or the accuracy of the // best result is wider than the acceptable maximum distance, request a single update. // This check simply implements the same conditions we set when requesting regular // location updates every [minTime] and [minDistance]. if (locationListener != null && (bestTime < minTime || bestAccuracy > minDistance)) { IntentFilter locIntentFilter = new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION); context.registerReceiver(singleUpdateReceiver, locIntentFilter); locationManager.requestSingleUpdate(criteria, pendingIntent); } return bestResult; } public void setLocationListener(LocationListener li) { locationListener = li; } /** * This {@link BroadcastReceiver} listens for a single location * update before unregistering itself. * The oneshot location update is returned via the {@link LocationListener} */ protected BroadcastReceiver singleUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { context.unregisterReceiver(singleUpdateReceiver); String key = LocationManager.KEY_LOCATION_CHANGED; Location location = intent.getParcelableExtra(key); if (locationListener != null && location != null) locationListener.onLocationChanged(location); locationManager.removeUpdates(pendingIntent); } }; public void cancel() { context.unregisterReceiver(singleUpdateReceiver); locationManager.removeUpdates(pendingIntent); } }