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
Related
How can you use LocationManager.GetCurrentLocation with Xamarin.Android?
Specifically, I can't figure out how to use the IConsumer parameter required.
Here's the documentation for it:
https://learn.microsoft.com/en-us/dotnet/api/android.locations.locationmanager.getcurrentlocation?view=xamarin-android-sdk-12
EDIT:
If I leave my test device (Android 6) on my desk, next to a window, it'll only ever return null locations until I walk around with the device. Network reception is good, and if I return to the exact same location once it's "woken up" then GPS signal is good. This is true even for the lowest accuracy request.
Here's an example of the code I'm using (have tried various timeouts, and polling continuously):
Here's the code I'm using:
// Get the last known location, from the OS cache
Location cached = await Geolocation.GetLastKnownLocationAsync();
// Location found?
if (cached != null)
OnLocationFound(cached);
// Get current location
GeolocationRequest networkRequest = new GeolocationRequest(GeolocationAccuracy.Lowest, timeout);
Location network = await Geolocation.GetLocationAsync(networkRequest, cancellationToken.Token);
// Location found?
if (network != null)
OnLocationFound(network);
// Get current location
GeolocationRequest gpsRequest = new GeolocationRequest(GeolocationAccuracy.Medium, timeout);
Location gps = await Geolocation.GetLocationAsync(gpsRequest, cancellationToken.Token);
// Location found?
if (gps != null)
OnLocationFound(gps);
if (ActivityCompat.checkSelfPermission(super.getContext(), Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(super.getContext(), Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(super.getActivity(), new String[]
{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION);
} else {
Location location = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (location != null) {
latitude = String.valueOf(location.getLatitude());
longitude = String.valueOf(location.getLongitude());
LogUtils.v(TAG, "Latitude : " + latitude);
LogUtils.v(TAG, "Longitude : " + longitude);
}
}
Sometimes I get the value, but then I do not know what happens that gives me null.
Android documentation says that Location can be null in these cases:
Location is turned off in the device settings. The result could be
null even if the last location was previously retrieved because
disabling location also clears the cache.
The device never recorded its location, which could be the case of
a new device or a device that has been restored to factory
settings.
Google Play services on the device has restarted, and there is no
active Fused Location Provider client that has requested location
after the services restarted. To avoid this situation you can create
a new client and request location updates yourself.
I am trying to get my current coordinates with network provider and not gps provider.
I was able to figure out the solution for that but I am a bit confused with the concept in this scenario.
Working Code
Here's my code for getting my coordinates:
public void getLocation(){
locationManager = (LocationManager)getSystemService(LOCATION_SERVICE);
if(appUtils.isOnline()){
try{
Geocoder geocoder = new Geocoder(
MainActivity.this.getApplicationContext(),
Locale.getDefault());
Location locationNetwork = locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
List<Address> list;
if(locationNetwork!=null){
Toast.makeText(context,"Network Available!!",Toast.LENGTH_LONG).show();
list = geocoder.getFromLocation(locationNetwork.getLatitude(),locationNetwork.getLongitude(),3);
if(list!=null&&list.size()>0){
latitude = list.get(0).getLatitude();
longitude = list.get(0).getLongitude();
Toast.makeText(context,String.valueOf(latitude) + " (....) " + String.valueOf(longitude),Toast.LENGTH_LONG).show();
int count = 0;
while (latitude==null||longitude==null){
latitude = list.get(count).getLatitude();
longitude = list.get(count).getLongitude();
count++;
Toast.makeText(context,String.valueOf(latitude) + " --- " + String.valueOf(longitude),Toast.LENGTH_LONG).show();
}
}
}else{
Toast.makeText(context,"No response!!",Toast.LENGTH_LONG).show();
}
}catch (IOException e){
e.printStackTrace();
}
}else{
Toast.makeText(context,"Server not responding",Toast.LENGTH_LONG).show();
}
}
This piece of code is working perfectly fine when the gps is enabled. If gps is disabled, it doesn't work.
Now, if we are setting the location to NETWORK_PROVIDER:
Location locationNetwork = locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
Why do we still require gps ?
Now if I change it to PASSIVE PROVIDER:
Location locationNetwork = locationManager
.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
It works fine with the passive provider. Why is it that ?
Can someone explain what is the major difference here and what would be the right way to get the coordinates with network provider ?
I know this question is been asked several times and I did went through it. I just want to get cleared with this concept.
Thank's in advance.. :)
It doesn't require GPS to use the network provider, I've done it many times. However, getLastKnowLocation may not return a value if either it has never had an app request updates for that provider, or if the last time that happened was too long ago. You cannot count on that function always returning non-NULL. If you want to ensure that you get a location, use requestSingleUpdate instead. This will always get you a location (assuming the provider you use is enabled), but may take some time- a result may not be immediately available.
(There is one other time that function may never return- if you use the GPS provider and it can't get a lock on enough sattelites to find a location. Such as if you're in an underground parking garage).
This is the bit of code that I use to quickly get the current location, by checking all available network options.
private double[] getGPS(){
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
List<String> providers = lm.getProviders(true);
/* Loop over the array backwards, and if you get an accurate location, then break out the loop*/
Location l = null;
for (int i=providers.size()-1; i>=0; i--) {
l = lm.getLastKnownLocation(providers.get(i));
if (l != null) break;
}
double[] gps = new double[2];
if (l != null) {
gps[0] = l.getLatitude();
gps[1] = l.getLongitude();
}
return gps;
}
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");
}
}
}
...
}
I have an application that used to locate a user's location with this code
public static Location getCurrentLocation(Context context) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
Criteria c = new Criteria();
String provider = lm.getBestProvider(c, true);
Location location;
if (provider == null) {
location = new Location("gps");
location.setLatitude(U.LATITUDE);
location.setLongitude(U.LONGITUDE);
} else {
location = lm.getLastKnownLocation(provider);
if (location == null) {
location = new Location("gps");
location.setLatitude(U.LATITUDE);
location.setLongitude(U.LONGITUDE);
}
}
return location;
}
This code appears to only be returning the behavior when provider is null or location is null. My guess is that something changed in this code.
String provider = lm.getBestProvider(c, true);
I have some code very similar to yours and it stopped working on my device after the upgrade to Android 4.4 (i.e. getBestProvider returned null which it never did before).
I discovered the reason when I saw that another App displayed that the NetworkProvider wasn't available (although I had perfect connection to both wifi and 3G) and it could not get a GPS signal (obviously, because I was indoors). Then I toggled Android's location quick setting and a dialog popped up, asking me to agree to the new location management of Android 4.4 (see https://support.google.com/nexus/answer/3467281). Afterwards, my code worked as before.