Geofences not available and how to handle it - android

I'm working with geofences on android, it's all working fine on most phones,
but on some of them, it's just not working (showing "geofences not available" in my error logs).
Some users don't have their location tracking enabled for Google Play Services.
I think that is the reason why geofencing is not working on their phones. (right?)
Now I want a way to detect this. (inform the user and eventually show them how to fix it)
I know there is a way to know wether google play services is available or not (and I already do this), but that doesn't say anything about the location services being enabled or not.
I know that I can check it while adding or removing geofence, there is an error code for it (geofence not available Location Status Codes), but that's not enough.
Some users disable the location services after they added the geofences. Then I have no way of knowing this, and my app won't work. (complaining users etc.).
At least I have to inform the user when they open the app to check what's wrong.
Does someone have an idea on how to do this?
The best I can do now is adding and removing a dummy geofence on app boot, but there has to be a better way?
EDIT:
I tested it with a device that had the issue, removing the app and reinstalling seems to fix the problem.
I'm not doing anything special on the first boot, so this is really weird.
It looks as if there is a problem with the google play services connection, and reinstalling the app does something special with these services.
Is this possible? It gives no errors while connecting to them, but it did when I tried to set geofences (see above).

The geofences will get disabled (and unregistered) if the GPS Provider or the Network Provider are disabled for whatever reason. That will cause your onAddGeofencesResult() to get called with an status code of error (1000). You can detect it right there.
Or, you detect this by implementing a Receiver that gets called in response to android.location.PROVIDERS_CHANGED. Remember to add the receiver to the AndroidManifest
<receiver android:name=".service.GeofenceProvidersChangedBroadcastReceiver">
<intent-filter>
<action android:name="android.location.PROVIDERS_CHANGED"/>
</intent-filter>
</receiver>
On the receiver implementation, on its onReceive(), get the location manager and test for the providers status:
public class ProvidersChangedBroadcastReceiver extends WakefulBroadcastReceiver {
:
:
#Override
public void onReceive(Context context, Intent intent) {
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
// GPS_PROVIDER IS enabled...
} else {
// GPS_PROVIDER is NOT enabled...
}
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
// NETWORK_PROVIDER IS enabled...
} else {
// NETWORK_PROVIDER is NOT enabled...
}
:
:
:
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) &&
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
// We are good for geofencing as both GPS and Network providers are enabled....
}
:
:
:
}

Related

How to get device location

So, I'm trying to build a weather app for practice and I've been running into the problem of trying to get the location. I've read people suggest to use getLastLocation through the fused location API, problem with that is if they don't already have a location registered on the device it comes up null. I've noticed using the emulator times that this come up is rare, but I'd still like my app to handle it properly. One instance where you might run into this is if they just turned GPS off and back on, or if the phone was just turned on. One thing I did was if getLastLocation does come back null, is to request an update, but then you still have to wait for the device to register an updated location, which with a weather app all of the data is based off of and you're still kind of running into the same problem. I've noticed with other apps this isn't a problem, like sometimes I actually have to load up Google Maps to get it to register a location. How does Google Maps force it to update the location? Here's the example from my getLocation method:
public void getLocation() throws SecurityException {
boolean gps_enabled;
LocationManager lm = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (gps_enabled) {
Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location != null) {
startGetForecast(location);
} else {
LocationRequest request = LocationRequest.create();
request.setNumUpdates(1);
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, request, this);
}
}
else {
AlertDialogFragment alertDialogFragment = new AlertDialogFragment();
alertDialogFragment.setErrorTexts("Location Unavailable", "Could not retrieve location information.", "Exit");
alertDialogFragment.show(getSupportFragmentManager(), "Location Unavailable");
}
}
Note: This answer could sound like not answering to the question directly but after chatting (comments in question) with OP as his code was ok in general but the condition around the problem, this answer has satisfied OP.
Fortunately, the standard library Is not the only way Google can get
code into your hands. In addition to the standard library, Google
provider Play services. This is a set of common services that are
installed alongside the Google Play store application. To fix this
location mess, Google shipped a new locations services in Play
Services called the Fused Location Provider.
Since these libraries
live in another application, you must actually have that application
installed. This means that only devices with the Play Store app
installed and up to date will be able to use your application.
So the conclusion is:
You need to test the app on your device as mentioned above.
Since you are using Fused Location Provider Api, that means the Api will automatically determine last location from one of the following sources:
GPS radio
Coarse points from cell towers
WiFi connections
So you could easily remove the GPS if condition from your code
Be a ware of that if you must use GPS signal, you need to be out side the building, lab, home or office.
If you want to dig more find more in the mentioned resources and there is a lot of online resources.
Resources: The first part of the answer, the source of it is from:
Android Programming: The Big Nerd Ranch Guide 2nd edition chapter 31 page 552 under Google Play Services

Prevent user to change GPS status Fused Location API

I'm using the Google API (Fuse Location Provider) to track a device's location but I need to prevent the user from disabling the GPS. I read a lot of answers but all I've found is: No, can't do, rooting the device or bug exploits. A friend just showed me a parental control app that accomplishes this without having to root the device. About user privacy, etc. The code will be used only in company's devices with the users being aware of this.
At least I need a way to detect if the GPS has been deactivated to notify the user, by the way I can't find any reference to this in the Google API.
Thanks a lot in advance.
GpsStatus.Listener - Try reading this.
You can override the method onGpsStatusChanged:
#Override
abstract void onGpsStatusChanged(int event){
if (event==GPS_EVENT_STOPPED){
LocationManager mlocManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
boolean enabled = mlocManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if(!enabled){
//toggle your notification
}
}
}

Not seeing Geofence Transitions in Google's tutorial on Geofencing

I am trying to test Geofence functionality using Google's example: Creating & Monitoring Geofences. I have uploaded the code here.
Problem is that I never get a notification of entry or exit. I have Wifi, 4G and GPS on. I have tried to test in the following ways:
I even walk out of my house for about 50ft and walk back in - but no notifications. I can see that play services gets connected and even the "GeofenceUtils.ACTION_GEOFENCES_ADDED in MainActivity.java" gets triggered, so I think Geofences are getting added correctly.
Enabled Mock Locations in the Settings and used this Fake GPS App and changed location - starting from the same coordinates as the Geofence1 and then setting to something totally outside (in another state) - but I still dont get an exit notification.
What am I doing wrong? ANyone had success in running this Google's example: Creating & Monitoring Geofences. I have uploaded the code here for easy browsing.
I am just trying to learn Geofencing - detecting entry & exit. I WILL MARK ANY ANSWER AS THE RIGHT ANSWER THAT I CAN USE TO GET GEOFENCING WORKING ON MY REAL DEVICE
.
I was also having issues with the example code. One of the main issues I had was around declaring the ReceiveTransitionsIntentService. You don't need to add a BroadcastReceiver or request location updates, but you need to change your AndroidManifest to set the exported value to true, like so:
<service
android:name=".ReceiveTransitionsIntentService"
android:exported="true" >
</service>
Make sure that the path to the service is also correct.
I've simplified the Google example significantly and you can find the code and explanation here. I've removed the UI component and persistent Geofence storage, so this example should be easier to follow. Hope this helps!
Initally my code was running smoothy but after that I too get this problem , Please try this by modifying geofenceRequester class in continueAddGeofences() function.
basically it is calling the receiver through location request method
private void continueAddGeofences() {
// Get a PendingIntent that Location Services issues when a geofence
// transition occurs
mGeofencePendingIntent = createRequestPendingIntent();
// Send a request to add the current geofences
mLocationClient.addGeofences(mCurrentGeofences, mGeofencePendingIntent,
this);
//mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
try {
LocationRequest locationrequest = LocationRequest.create();
locationrequest.setPriority(locationrequest.PRIORITY_HIGH_ACCURACY);
locationrequest.setInterval(3000);
mLocationClient.requestLocationUpdates(locationrequest, mGeofencePendingIntent);
} catch (Exception e) {
e.printStackTrace();
}
I was also having the same problem with Geofence api. At the end, I used addProximityAlert of LocationManager.It did work very well!

Getting the message GEOFENCE_NOT_AVAILABLE when using Google Play Services API on Android

I downloaded the GeoDetection.zip file for trying out location based geofencing using the Google Play Services API that is available from the developer android website from the following link:
http://developer.android.com/training/location/geofencing.html
but when i tried to add geo fences using this app, i constantly get the error GEOFENCE_NOT_AVAILABLE
i tried to check what this error means, and i found that if location access is not available that is when i should get this message, but i checked the Location Services section on my phone and i saw that all the Location provides GPS/Wifi Location are all enabled. I am also connected to WIFI successfully. Not sure why i still get the message.
Any body else encounter this issue?..if so, do you have a fix that i could try?
Any other suggestions?.. i tried a lot of options to make sure it is capturing all the options, but there is isn't much debug information for me to proceed any further.
Thanks in advance
Are you requesting the FINE_LOCATION permission?
Remember on Android 6 and above you have to handle the permissions at run-time too.
The above error, GEOFENCE_NOT_AVAILABLE is an expected error code, to let you know that the location adapter is off and location (and geofence) will NOT be tracked anymore.
Once available again, your geofences will be active and you will get the desired callback.
Few Reasons why the triggers aren't happening :
Check if your expiry timestamp that you set in your geofence request, is still valid.
Did your device get restarted ? If yes, you need to re-register fences. (Refer documentation : Re-register geofences only when required documentation)
Is your location, set to High Accuracy mode in Device settings ?
When you re-register your fences, you should un-register them first and then only re-register.
Open Google maps and check if it's showing the right location.
Is Battery Optimisation on for your device ? This will delay geofence triggers
Is you app whitelisted for background operations (Happens in some Chinese OEM's)

LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) is not reliable, why?

One user of my app reported that app tells network for location is off even he did turn it on. He sent me few screen shots and they made me think;
LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
is not working properly. His phone is running Android 4.1.2 and first I thought this is the cause of this issue. But it was not the case. He sent me a screen shot of that setting too.
Then I googled and found this. The question seems to help me but unfortunately answer was not helpful for this case and questioner did not pursue farther.
My app is related to location and have been using LocationManager.isProviderEnabled to know GPS and Network for location is on or off. I have never been told my app is not properly knowing those settings until recently. He is the first user who reported the issue. I learned there are another method to know GPS and Network for location settings, by seeing Secure.LOCATION_PROVIDERS_ALLOWED. To see how this method work on his phone, I wrote simple app and asked him to run. This app does simple task and shows text on screen.
LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
if(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
{
string = "GPS=on\n";
}
else
{
string = "GPS=off\n";
}
if(locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
{
string += "Network=on\n";
}
else
{
string += "Network=off\n";
}
String status = android.provider.Settings.Secure.getString(getContentResolver(), Secure.LOCATION_PROVIDERS_ALLOWED);
if(status.contains("gps"))
{
string += "GPS=on\n";
}
else
{
string += "GPS=off\n";
}
if(status.contains("network"))
{
string += "Network=on\n";
}
else
{
string += "Network=off\n";
}
He sent back screen shot again. It looks;
GPS=on
Network=off
GPS=on
Network=on
This result did not make me happy. There could be some possibilities for this.
As other person questioned before, this issue has been there on some phones.
Google broke this with 4.1.2. isProviderEnabled does not work on this version.
Although not documented, starting 4.1.2, isProviderEnabled won't work as it did before.
No, Google changed anything. This is a bug for this particular phone.
Now my questions are;
Is LocationManager.isProviderEnabled still valid for Android 4.1.2 and later?
Does seeing Secure.LOCATION_PROVIDERS_ALLOWED have some drawbacks/pit holes (when I gave up using LocationManager.isProviderEnabled?
Thanks in advance.
EDIT1:
Here you can download test app from Google Play to try or ask someone to try.
EDIT6:
I removed test app since this question is answered.
EDIT2:
I released my app which checks network provider is usable by seeing Secure.LOCATION_PROVIDERS_ALLOWED and got exception on limited phones.
These are ACRA's report.
Some phone running OS 4.1.1.
java.lang.IllegalArgumentException: requested provider network doesn't exisit
at android.os.Parcel.readException(Parcel.java:1434)
at android.os.Parcel.readException(Parcel.java:1384)
at android.location.ILocationManager$Stub$Proxy.requestLocationUpdates(ILocationManager.java:675)
at android.location.LocationManager._requestLocationUpdates(LocationManager.java:686)
at android.location.LocationManager.requestLocationUpdates(LocationManager.java:508)
Some phone running OS 4.1.2.
java.lang.IllegalArgumentException: provider=network
at android.os.Parcel.readException(Parcel.java:1439)
at android.os.Parcel.readException(Parcel.java:1389)
at android.location.ILocationManager$Stub$Proxy.requestLocationUpdates(ILocationManager.java:659)
at android.location.LocationManager._requestLocationUpdates(LocationManager.java:690)
at android.location.LocationManager.requestLocationUpdates(LocationManager.java:512)
I have never seen those exceptions until I changed a method to check network provider for location is usable or not. So I think LocationManager.isProviderEnabled is safe and seeing Secure.LOCATION_PROVIDERS_ALLOWED is risky. But this will put me back to original issue. Why LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) returns false (and there is not really) when Secure.LOCATION_PROVIDERS_ALLOWED tells there IS. Is Android OS poorly designed? Or I have just seeing issues tied only to specific (but there are at least 2 of them) phones?
EDIT3:
I updated test app to show GPS/Network location provider seems really usable or not by accessing with requestLocationUpdates().
And I disclose 2 phones name.
1) SBM200SH, OS4.1.2, Softbank mobile, Sharp Corporation
2) HTX21 (INFOBAR A02), OS4.1.1, KDDI, HTC
EDIT4:
I found 3rd phone.
3) SBM203SH, OS4.1.2, Softbank mobile, Sharp Corporation
EDIT5:
Sharp Corporation is running discussion space for mobile developers. I posted topic by presenting this SO's question. I hope someone at Sharp Corporation takes action for this. I will keep this updated.
Developer support provided by Sharp corporation is excellent and they answered to my question in less than 48 hours.
This is what I got from them.
There are 2 conditions must be met that LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) returns true.
Some internal state is ready for network location.
Network location is enabled on setting screen.
Second one is obvious. But first one is not. They told how to simulate first one is negative. You can confirm the issue with steps shown below and running my test app (please see my question for link to download).
Open settings of you phone.
Tap Applications.
Tap All tab.
Find "Network Location", tap it.
Tap "Disable".
Reboot your phone.
Run test app.
For reason I can't understand the user's phone failed to do something related to first condition shown above and exhibits the issue.
Conclusion:
LocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) is reliable. And be aware, Secure.LOCATION_PROVIDERS_ALLOWED is less reliable.
The modern way to check the users Location settings is through LOCATION_MODE in Settings.Secure
For example if you simply want to know if the user has disabled them or not, you can do:
public static boolean isLocationEnabled(Context context) {
return getLocationMode(context) != Settings.Secure.LOCATION_MODE_OFF;
}
private static int getLocationMode(Context context) {
return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
}
This will return true if Location is enabled. If you need finer granularity see the docs for details.
This method is better suited when using the Google Services Location APIs than the old NETWORK_PROVIDER and GPS_PROVIDER ways. Note: Requires KitKat / API19
Not directly an answer to your question(s), but check out the new Location API that Google launched last week. It's really easy to implement and it will check for the best possible location without wasting battery.
http://developer.android.com/google/play-services/location.html
and here's a session at Google I/O about this new API and how to use it.
http://www.youtube.com/watch?v=Bte_GHuxUGc
This way you don't need to worry about checking if the GPS is on or not and stuff like that
Location Manager is not reliable on some phones. You may notice that if you launch google maps all of a sudden your app works. That is because Google Maps kicked the LocationManager. Which also means that there is programmatic way to kick that dude alive. So I used
HomeScreen.getLocationManager().requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, new LocationListener() {
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onLocationChanged(final Location location) {
}
});
After the above code, I called what ever I needed from LocationManager and it kinda worked. If not try out the new API's LocationClient. This is suppose to be much better, battery, accuracy and reliability.

Categories

Resources