Get google map accurate location - android

I have following code which return user current location, but the location is not accurate
lateinit var mFusedLocationClient: FusedLocationProviderClient
override fun onCreateView(...) {....}
#SuppressLint("MissingPermission")
private fun getLastLocation() {
mFusedLocationClient.lastLocation
.addOnCompleteListener(requireActivity()) { task ->
if (task.isSuccessful && task.result != null) {
mLastLocation = task.result
mMap.isMyLocationEnabled = true
val driverLatLng = LatLng(mLastLocation.latitude, mLastLocation.longitude)
markerOptions.position(driverLatLng).title("Your location").icon(
BitmapDescriptorFactory.fromBitmap(
driverIcon()
)
)
mMap.addMarker(markerOptions)
} else {
Log.w("location", "getLastLocation:exception", task.exception)
}
}
}
Questions
How to make current location more accurate
How to make blue circle more small, just to cover around current location pin (marker)?

Question 1)
If you are not requesting the highest accuracy possible you need to set it to this value:
LocationRequest.PRIORITY_HIGH_ACCURACY
This way the provider should use the device GPS if available instead of Wi-Fi/cell towers.
Official docs here. A code sample using high accuracy can be found here.
Question 2)
If I got your question right the circle represents the accuracy of the current location so with a higher precision the circle I suppose that the circle would become smaller. You just have to give it a try with the highest possible accuracy (just be sure that the device's GPS is being used and that it has a good signal)

Related

Location-Manager not returning location consistently

I am using Network Provider to get the last known location of the user. When I tested it, It was working perfectly in one of my phones, but latitude and longitude is returning null on another phone. Is there an alternative to location manager to get a more consistent result. Or is there some other bug in my code
Here is my Permission checking Code:
if (ContextCompat.checkSelfPermission(
applicationContext,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
AlertDialog.Builder(this)
.setCancelable(false)
.setTitle("Please Grant Location Permissions")
.setPositiveButton("Ok", null)
.show()
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
101
)
}
And my location request code
longitude =
locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)?.longitude
latitude =
locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)?.latitude
val geocoder = Geocoder(this)
if (latitude != null && longitude != null) {
val addresses: List<Address> = geocoder.getFromLocation(latitude, longitude, 1)
val cityName = addresses[0].subAdminArea
}
As documentations says
Gets the last known location from the given provider, or null if there is no last known location. The returned location may be quite old in some circumstances, so the age of the location should always be checked.
This will never activate sensors to compute a new location, and will only ever return a cached location.
See also getCurrentLocation(String, CancellationSignal, Executor, Consumer) which will always attempt to return a current location, but will potentially use additional power in the course of the attempt as compared to this method.
Params:
provider – a provider listed by getAllProviders()
Returns:
the last known location for the given provider, or null if not available
Throws:
SecurityException – if no suitable permission is present
IllegalArgumentException – if provider is null or doesn't exist
so in case if last location return null you must request new on or custom handler

Getting GPS location Consumes battery

I am working on a android project where i have to get the current location of the user for every 2 minutes and the code we are using is
try {
DpGPSEnabled = DpLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
DpNetworkEnabled = DpLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (DpIsGpsProvider && DpGPSEnabled) {
DpLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,DpMIN_TIME_BW_UPDATES,DpMIN_DISTANCE_CHANGE_FOR_UPDATES,DpLocationListener);
} else if (!DpGPSEnabled) {
DpLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,DpMIN_TIME_BW_UPDATES,DpMIN_DISTANCE_CHANGE_FOR_UPDATES,DpLocationListener);
DpGenUtil.setLocationPostion(DpLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
}
if (DpGPSEnabled && DpNetworkEnabled) {
DgProvider = "GPS+Network";
} else if (DpNetworkEnabled) {
DgProvider = "Network";
} else if (DpGPSEnabled) {
DgProvider = "GPS";
} else {
DgProvider = "Not Selected";
}
} catch (Exception ex) {
}
where DpMIN_TIME_BW_UPDATES =1000*60*2; 2 minutes
and DpMIN_DISTANCE_CHANGE_FOR_UPDATES = 200; in meters
the problem is we are able to get the locations properly , but battery started to drain faster , is there any other method we can get the location and it will not consume much battery also.
Gps consumes more battery than wifi. You can request location updates using Criteria with LocationManager or you can use FusedLocationProvider api if not having Google Play Services is not an issue. FLP uses Wifi, Gps and Kalman filters for location accuracy. You can test both LocationManager and FLP for accuracy and battery consumption. I get up to 4m accuracy(better devices may get better results) on outdoors from both LocationManager Gps and FLP. LocationManager wifi has less accuracy and does not have speed, altitude info.

Xamarin, Android: Why am I getting wrong coordinates?

So I am having this problem - the below code is part of a class in my app.
Now, this class gives me coordinates that are ABOUT right to my location. Always a few 100 meters away from where I really should be. Why is this happening? Is this maybe because I dont ask for a "fine" accuracy?
THANKS :)
public void OnLocationChanged(Location location)
{
_currentLocation = location;
{
// this is needed for my mocked location
GlobalElapsedRealTime = _currentLocation.ElapsedRealtimeNanos;
GlobalLatitude = _currentLocation.Latitude;
GlobalLongitude = _currentLocation.Longitude;
// Log.Debug("2", "Your Real Location is at " + GlobalLongitude + " // " + GlobalLatitude);
}
}
public void InitializeLocationManager()
{
_locationManager = ctxt.GetSystemService(Context.LocationService) as LocationManager;
if (_locationManager.AllProviders.Contains(LocationManager.NetworkProvider)
&& _locationManager.IsProviderEnabled(LocationManager.NetworkProvider))
{
_locationProvider = LocationManager.NetworkProvider;
Log.Debug("1", "Location Manager has been initialized!");
}
else
{
_locationProvider = String.Empty;
}
}
public void StartLocationUpdates()
{
_locationManager.RequestLocationUpdates(_locationProvider, 0, 0, this);
}
It's because you are using the LocationManager.NetworkProvider. This guesses your location based of cell towers and known WiFi hotspots and is not particularly accurate. Being a few hundred meters out sounds about right. Useful for working out which city or suburb you are in maybe but not for more accurate tracking.
Try using LocationManager.GPSProvider and make sure the ACCESS_FINE_LOCATION permission is enabled. It may take a bit longer to get a fix but it'll be much more accurate. How accurate depends on the device it's run on (phone GPS can be pretty good but not totally accurate as the chips are usually chosen based off price), and how much of a fix you can get - tall buildings can interfere with it for example.
More details:
https://developer.android.com/reference/android/location/LocationManager.html#GPS_PROVIDER

Android - Fused Location Provider API issues with satellite information (count, signal, etc)

I am working on a project where we are trying to track the position of a device and keep the data for later use. Before I talk about the issue I would like to provide some background.
By searching around StackExchange and Google and everywhere else, I have come to the conclusion that it is virtually impossible to get information about the satellites using the Fused Location API (good job there Google).
The method that most people are using is to actually use a LocationManager along side the Fused location to get the GPS Status. My first question comes here:
How can we be 100% sure that the numbers provided by the LocationManager are in sync with what the Fused Location has given us? Does the Fused Location use the Manager internally?
And now the issue. The app is using an "always on" sticky service to pick up the positions no matter what. When there are no satellites everything works as intended. Placing the device to a position where it can see satellites it does not seem to have a lock. Using the debugger the GpsStatus.getSatellites() brings an empty list. Now, without moving the device I start the app Compass (by Catch.com as there are many) that has a GPS type compass scheme. That one locks the satellites, and quite fast, and from that moment on my app also reports the satellites. If the compass is closed then the app gets stuck on the last number the Compass was providing!!! The device I am personally using for testing is a Nexus 7 2013 with its latest official updates (Android 6.0.1).
Here is some code:
public class BackgroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
GpsStatus.Listener,
LocationListener {
// Constants here....
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private LocationManager locationManager;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private NotificationManagement myNotificationManager;
private Boolean servicesAvailable = false;
//And other variables here...
#Override
public void onCreate()
{
super.onCreate();
myNotificationManager = new NotificationManagement(getApplicationContext());
myNotificationManager.displayMainNotification();
mInProgress = false;
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval
mLocationRequest.setInterval(PREFERRED_INTERVAL);
// Set the fastest update interval
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
servicesAvailable = servicesConnected();
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.addGpsStatusListener(this);
setUpLocationClientIfNeeded();
}
/**
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
protected synchronized void buildGoogleApiClient()
{
this.mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private boolean servicesConnected()
{
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode)
{
return true;
}
else
{
return false;
}
}
public int onStartCommand(Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress)
{
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
private void setUpLocationClientIfNeeded()
{
if (mGoogleApiClient == null)
buildGoogleApiClient();
}
public void onGpsStatusChanged(int event)
{
}
// Define the callback method that receives location updates
#Override
public void onLocationChanged(Location location)
{
simpleGPSFilter(location);
}
// Other fancy and needed stuff here...
/**
* "Stupid" filter that utilizes experience data to filter out location noise.
* #param location Location object carrying all the needed information
*/
private void simpleGPSFilter(Location location)
{
//Loading all the required variables
int signalPower = 0;
satellites = 0;
// Getting the satellites
mGpsStatus = locationManager.getGpsStatus(mGpsStatus);
Iterable<GpsSatellite> sats = mGpsStatus.getSatellites();
if (sats != null)
{
for (GpsSatellite sat : sats)
{
if (sat.usedInFix())
{
satellites++;
signalPower += sat.getSnr();
}
}
}
if (satellites != 0)
signalPower = signalPower/satellites;
mySpeed = (location.getSpeed() * 3600) / 1000;
myAccuracy = location.getAccuracy();
myBearing = location.getBearing();
latitude = location.getLatitude();
longitude = location.getLongitude();
Log.i("START OF CYCLE", "START OF CYCLE");
Log.i("Sat Strength", Integer.toString(signalPower));
Log.i("Locked Sats", Integer.toString(satellites));
// Do the math for the coordinates distance
/*
* Earth's radius at given Latitude.
* Formula: Radius = sqrt( ((equatorR^2 * cos(latitude))^2 + (poleR^2 * sin(latitude))^2 ) / ((equatorR * cos(latitude))^2 + (poleR * sin(latitude))^2)
* IMPORTANT: Math lib uses radians for the trigonometry equations so do not forget to use toRadians()
*/
Log.i("Lat for Radius", Double.toString(latitude));
double earthRadius = Math.sqrt((Math.pow((EARTH_RADIUS_EQUATOR * EARTH_RADIUS_EQUATOR * Math.cos(Math.toRadians(latitude))), 2)
+ Math.pow((EARTH_RADIUS_POLES * EARTH_RADIUS_POLES * Math.cos(Math.toRadians(latitude))), 2))
/ (Math.pow((EARTH_RADIUS_EQUATOR * Math.cos(Math.toRadians(latitude))), 2)
+ Math.pow((EARTH_RADIUS_POLES * Math.cos(Math.toRadians(latitude))), 2)));
Log.i("Earth Radius", Double.toString(earthRadius));
/*
* Calculating distance between 2 points on map using the Haversine formula (arctangent writing) with the following algorithm
* latDifference = latitude - lastLatitude;
* lngDifference = longitude - lastLongitude;
* a = (sin(latDifference/2))^2 + cos(lastLatitude) * cos(latitude) * (sin(lngDifference/2))^2
* c = 2 * atan2( sqrt(a), sqrt(1-a) )
* distance = earthRadius * c
*/
double latDifference = latitude - lastLatitude;
double lngDifference = longitude - lastLongitude;
double a = Math.pow((Math.sin(Math.toRadians(latDifference / 2))), 2) + (Math.cos(Math.toRadians(lastLatitude))
* Math.cos(Math.toRadians(latitude))
* Math.pow((Math.sin(Math.toRadians(lngDifference / 2))), 2));
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = earthRadius * c;
Log.i("New point distance", Double.toString(distance));
// Filter logic
// Make an initial location log
if ((!isInit) && (myAccuracy < ACCEPTED_ACCURACY))
{
isInit = true;
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
}
else
{
// Satellite lock (use of GPS) on the higher level
if (satellites == 0)
{
// Accuracy filtering at the second level
if (myAccuracy < ACCEPTED_ACCURACY)
{
if ((distance > ACCEPTED_DISTANCE))
{
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
Log.i("Location Logged", "No Sats");
/*
// Calculate speed in correlation to perceived movement
double speed = distance / (PREFERRED_INTERVAL / 1000); // TODO: Need to make actual time dynamic as the fused location does not have fixed timing
if (speed < ACCEPTED_SPEED)
{
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
} */
}
}
}
else if ((satellites < 4) && (signalPower > ACCEPTED_SIGNAL))
{
if (myAccuracy < (ACCEPTED_ACCURACY + 50))
{
logLocations(location);
Log.i("Location Logged", "With Sats");
}
}
else
{
if (myAccuracy < (ACCEPTED_ACCURACY + 100))
{
lastSpeed = mySpeed;
lastBearing = myBearing;
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
Log.i("Location Logged", "With Good Sats");
}
}
}
Log.i("END OF CYCLE", "END OF CYCLE");
}
private void logLocations(Location location)
{
String myprovider = "false";
String temp = timestampFormat.format(location.getTime());
MySQLiteHelper dbHelper = new MySQLiteHelper(getApplicationContext());
try
{
dbHelper.createEntry(latitude, longitude, allschemes, temp, mySpeed, myAccuracy, myBearing, myprovider, satellites);
}
catch (Exception e)
{
e.printStackTrace();
}
CheckAutoArrive(String.valueOf(latitude), String.valueOf(longitude));
}
This is the part of the code I think might be needed. I am leaving all the filtering code there along with the math to compute Earth's radius given the latitude and the distance between 2 points on the map. Feel free to use that if you need it.
In connection to the fact that the Compass app can actually make the system get satellites while my app cannot. Is there a way to actually force read the location services? Is it possible that the Fused Location actually uses GPS but the Location Manager does not know it?
Finally I would like to mention that the application has been tested in other devices (phones, not tablets) with different versions of Android and seems to be working properly.
Any ideas would be more than welcome. And of course go ahead and ask anything I might have forgotten to mention.
EDIT : My actual questions were hidden in the text so to lay them out:
1) Are the Location data we get from Fused Location and the rest of the GPS data we can, seemingly, only get from the Location Manager in sync or is there the possibility to get a Location but wrong number of locked satellites for the particular point?
2) What could be the reason behind the weird behavior where the application cannot get a lock to satellites but if the lock comes from another application it seems to be used properly by the application? To make this even weirder this happens to a Nexus 7 (Android 6.0.1) but not to other devices tested with different Android versions.
From my understanding:
1)
The FusedLocationApi returns a new location every time there is a new reading from any relevant provider on the client device (WiFi, CellTower, GPS, Bluetooth). This reading gets fused with the previous location estimate (probably using an extended Kalman filter or similar). The resulting location update is a fused estimate of several sources, which is why there is no metadata from individual providers attached.
So the Location data you get from the API may coincide with a pure GPS reading obtained from the LocationManager (if GPS was the most recent and relevant location source), but it doesn't have to. Consequently, the number of satellites obtained from the last pure GPS reading may apply to the latest Location returned by the FusedLocationApi or it may not.
In short:
There is no guarantee that a location reading obtained from the LocationManager is in sync with a location from the FusedLocationApi.
2)
First off: To pinpoint the root cause of this issue you need to test with several devices at several locations. Since you asked
What could be the reason behind the weird behavior?
I'll throw one theory out there: Assuming that the LocationManager and the FusedLocationApi work completely separately, the LocationManager may be struggling to obtain a fix because you're relying on GPS only.
Try using the NETWORK_PROVIDER in addition to GPS to speed up the time-to-first-fix (thus enabling the LocationManager to make use of Assisted GPS). Other apps (like the Compass app) are almost certainly doing this, which would explain why they are getting a quicker fix.
Note: After you start to receive GPS data, you can of course unregister the network provider. Or you keep it on but simply ignore its updates.
That's one possible explanation for the weird behavior. You are probably aware that location behavior is dependent on the device, OS, GPS chipset, firmware and location you are in - so if you plan to go manual (i.e. not use the FusedLocationApi) you will have to experiment quite a bit.
In addition to the answers, let me provide an opinionated take on your problem (to be taken with a grain of salt ;-): I think you're trying to combine two things that were made for very different use cases and aren't meant to be combined.
Obtaining the number of satellites is something utterly technical. No end user will ever be interested in this kind of info, unless your application teaches them about GNSS. If you want to record it for internal analytics purposes that's fine, but then you must be able to deal with situations where this info is not available.
Scenario 1: For some reason you decide that you absolutely need the detailed (technical) specifics of GPS readings. In that case, build the logic yourself, the old school way. I.e. ask for GPS readings (and possibly speed up this process by using the network provider) via the LocationManager, then fuse this stuff on your own. However, in this scenario don't ever touch the FusedLocationApi.
Even though it may seem old-fashioned and arcane nowadays, using the LocationManager with DIY fusion logic still makes perfect sense for a small number of use cases. That's why the API is still around.
Scenario 2: You simply want to get quick and accurate updates on the client's location. In that case specify your desired update frequency and accuracy and let the FusedLocationApi do its job. The FusedLocationApi has come a long way in the past years and chances are that nowadays it will be quicker and better at figuring out how to obtain location info than any DIY logic. This is because obtaining location info is a very heterogenous problem that depends on the capabilities of the client device (chipset, firmware, OS, GPS, WiFi, GSM/LTE, Bluetooth etc.) as much as on the physical surroundings (WiFi/CellTower/Bluetooth signals nearby, inside or outside, clear sky or urban canyon etc.).
In this scenario, don't touch the manual providers. If you do, don't expect to make any meaningful inferences on the relation between the readings of individual providers and the fused results.
Two final remarks:
Android provides Location.distanceTo() and Location.distanceBetween(), so there is no need to implement the Haversine formula in your code.
If you simply need quick and reliable updates from the FusedLocationApi, I have written a small utility class, called the LocationAssistant, that simplifies the setup and does most of the heavy lifting for you.

How frequently does the Android OS itself (not apps) poll for Location?

I'm looking at writing a location-aware application for Android. My application would periodically make calls to a central server, passing in the current user's location. I don't want to drain the battery, so I've been considering using the Passive Location Provider.
According to the provider description no active polling is performed (hence being labelled "Passive"). Instead, it relies on other applications requesting active polls.
My question is: If no other applications poll for Location, does it mean the Passive provider never provides any data? Or, does the Android OS itself periodically poll for Location. If so, what is the polling frequency?
Thanks
You can use the Google Play Services Location API. Its fused location provider will relieve you from such concerns. Forget about which providers to use, or how often the location is polled, or if there are other apps polling. Instead, specify high-level needs like "high accuracy" or "low power", and at which interval your app should be notified. Use listeners or intents to receive location updates from the location service. Here's a more recent article with some code.
AFAIK Android OS will not poll for location itself. If some apps polls it you can received the location then. See Android - Are there query-able services that already poll for GPS location? .
If no app has polled for location from a long time, last location known might be returned by the passive provider. If your application relies heavily on the location, then you might actually want to poll it yourself or if nothing is returned by the passive provider then you can yourself get the location. Frequency of getting the location can be 5 minutes( which is suggested by Google in the Android documentation). This frequency can vary based on your app's requirement.
Is you read the android documentation of requestLocationUpdates of LocationManager, it says:
Choosing a sensible value for minTime is important to conserve battery life. Each location update requires power from GPS, WIFI, Cell and other radios. Select a minTime value as high as possible while still providing a reasonable user experience. If your application is not in the foreground and showing location to the user then your application should avoid using an active provider (such as NETWORK_PROVIDER or GPS_PROVIDER), but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes) or greater. If your application is in the foreground and showing location to the user then it is appropriate to select a faster update interval.
That's my 2 cents regarding your question.
Android devices themselves never poll for users location, you need to do it yourself or rely on other apps to do it for you. You can possibly run the location update every 'x' min, using a GPS or network provider whatever deems fit (or maybe even both !)
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1500000, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1500000, 0, locationListener);
If you want to wait for other applications, or not want to drain the battery/search for users exact location you may use locationManager.getLastLocation();
This wont be always accurate, however it is the best you can hope for without actually running a location search
Though I don't believe that PASSIVE_LOCATION PROVIDERS will not get any location unless no other apps get a location, this is some post which is evangelizing it.
I believe that there would be some resident services part of the OS itself which listen to location changed events. because location services without maps, generally use GPS. But I recommend you to have a look at this discussion (probably trusted source).
as none of the posts here answer the OP question i'll make my three words here -
IT'S UP TO PROVIDER (TO PROVIDE THE LOCATION UPDATES) THUS PROVIDER DECIDES HOW OFTEN WILL REPORT THE LOCATION CHANGES TO LOCATON SERVICE
PASIVE - LIKE A PING PONG - to understand what it mean study flow of bellow methods
getLastLocation(LocationRequest request, String packageName)
reportLocation(Location location, boolean passive) -> updateLastLocationLocked(...) -> handleLocationChangedLocked(...)
ITS DEVELOPER JOB TO FILTER AND DECIDE IF PROVIDED LOCATION FULFILLS IT NEEDS
ad 3/ see all location provied details like:
PROVIDER NAME
ACCURACY,
TIME (MILLIS - EPOCH TIME),
SYSTEM ELAPSED NANOS (ELAPSED NANOS FROM DEVICE START)
ADDITIONAL DATA (EG IN BUNDLE LIKE SATELITES COUNT)
ETC...
ad 2/ LOCATION SERVICE IMPLEMENTATION ON AOSP:
package com.android.server;
/**
* The service class that manages LocationProviders and issues location
* updates and alerts.
*/
public class LocationManagerService extends ILocationManager.Stub {
...
// mapping from provider name to last known location
private final HashMap<String, Location> mLastLocation = new HashMap<>();
// same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS.
// locations stored here are not fudged for coarse permissions.
private final HashMap<String, Location> mLastLocationCoarseInterval = new HashMap<>();
...
#Override
public Location getLastLocation(LocationRequest request, String packageName) {
...
// Figure out the provider. Either its explicitly request (deprecated API's),
// or use the fused provider
String name = request.getProvider();
if (name == null) name = LocationManager.FUSED_PROVIDER;
LocationProviderInterface provider = mProvidersByName.get(name);
if (provider == null) return null;
...
Location location;
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
// Make sure that an app with coarse permissions can't get frequent location
// updates by calling LocationManager.getLastKnownLocation repeatedly.
location = mLastLocationCoarseInterval.get(name);
} else {
location = mLastLocation.get(name);
}
if (location == null) {
return null;
}
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
if (noGPSLocation != null) {
return new Location(mLocationFudger.getOrCreate(noGPSLocation));
}
} else {
return new Location(location);
}
}
return null;
....
private void handleLocationChangedLocked(Location location, boolean passive) {
if (D) Log.d(TAG, "incoming location: " + location);
long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
// Skip if the provider is unknown.
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) return;
updateLastLocationLocked(location, provider);
// mLastLocation should have been updated from the updateLastLocationLocked call above.
Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
Log.e(TAG, "handleLocationChangedLocked() updateLastLocation failed");
return;
}
// Update last known coarse interval location if enough time has passed.
Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
if (lastLocationCoarseInterval == null) {
lastLocationCoarseInterval = new Location(location);
mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
}
long timeDiffNanos = location.getElapsedRealtimeNanos()
- lastLocationCoarseInterval.getElapsedRealtimeNanos();
if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
lastLocationCoarseInterval.set(location);
}
// Don't ever return a coarse location that is more recent than the allowed update
// interval (i.e. don't allow an app to keep registering and unregistering for
// location updates to overcome the minimum interval).
Location noGPSLocation =
lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
// Skip if there are no UpdateRecords for this provider.
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null || records.size() == 0) return;
// Fetch coarse location
Location coarseLocation = null;
if (noGPSLocation != null) {
coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
}
// Fetch latest status update time
long newStatusUpdateTime = p.getStatusUpdateTime();
// Get latest status
Bundle extras = new Bundle();
int status = p.getStatus(extras);
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> deadUpdateRecords = null;
// Broadcast location or status to all listeners
for (UpdateRecord r : records) {
...
}
...
}
/**
* Updates last location with the given location
*
* #param location new location to update
* #param provider Location provider to update for
*/
private void updateLastLocationLocked(Location location, String provider) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
Location lastNoGPSLocation;
Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
lastLocation = new Location(provider);
mLastLocation.put(provider, lastLocation);
} else {
lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
if (noGPSLocation == null && lastNoGPSLocation != null) {
// New location has no no-GPS location: adopt last no-GPS location. This is set
// directly into location because we do not want to notify COARSE clients.
location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
}
}
lastLocation.set(location);
}
}
PASIVE PROVIDER:
/**
* A passive location provider reports locations received from other providers
* for clients that want to listen passively without actually triggering
* location updates.
*
* {#hide}
*/
public class PassiveProvider implements LocationProviderInterface {
...
#Override
public void setRequest(ProviderRequest request, WorkSource source) {
mReportLocation = request.reportLocation;
}
public void updateLocation(Location location) {
if (mReportLocation) {
try {
// pass the location back to the location manager
mLocationManager.reportLocation(location, true);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling reportLocation");
}
}
}
...
}

Categories

Resources