Cordova geolocation accuracy gets capped at 10 meters - android

Update: it is a Google Play Service issue, reported internally here and it will be fixed from version 13.4.0
We use the cordova gelocation plugin and the method navigator.geolocation.watchPosition() with the option enableHighAccuracy: true to track the user location and get the most accurate results.
Our app has been around for more than 1 year and we used to have no problems with any device getting a very good location accuracy, 4/6 meters when outside and the sky is clear.
Lately, many of our users are reporting not being able to get anything less than 10m accuracy no matter what they do.
We decided to test it ourselves and we found to have the same issue. Initially, we thought we introduced some bug in our latest release, we triple checked everything but we made no changes to code/dependencies involving geolocation.
We tested older versions of our app as well, where we are sure it was possible to get like 4m accuracy, but surprisingly they do not work either, accuracy is capped at 10m.
We tried different version of Android and we can reproduce the issue on any platform from 5 (Lollipop) to 8 (Oreo). We also have the same problem on iOS 10/11. Again, we have not updated the app in months.
There is a recent question about the same issue here:
Someone else is having the same problem using Android native code here
Does anyone know what is going on? Is it a permission issue? Location Services are set to High Accuracy as well.
For the sake of completeness, we are able to get 3/4 meters accuracy using the old version (2.x) of this plugin
Is it the only way to go?
We would rather not introduce an extra dependency for something that was working so well out of the box.
Many thanks

Looking at source code:
Old plugin (2.x) Source:
watchPosition: function(success, error, args) {
var win = function() {
var geo = cordova.require('cordova/modulemapper').getOriginalSymbol(window, 'navigator.geolocation');
geo.watchPosition(success, error, {
enableHighAccuracy: args[1]
});
};
exec(win, error, "Geolocation", "getPermission", []);
},
New Plugin (master) Source:
watchPosition: function(success, error, args) {
var pluginWatchId = utils.createUUID();
var win = function() {
var geo = cordova.require('cordova/modulemapper').getOriginalSymbol(window, 'navigator.geolocation');
pluginToNativeWatchMap[pluginWatchId] = geo.watchPosition(success, error, args);
};
var fail = function() {
if (error) {
error(new PositionError(PositionError.PERMISSION_DENIED, 'Illegal Access'));
}
};
exec(win, fail, "Geolocation", "getPermission", []);
return pluginWatchId;
},
In OLD plugin code enableHighAccuracy is a boolean set by (arg1 of array).
With NEW version of plugin you need to pass arg as JSON with that flag set: {enableHighAccuracy: true} to reproduce same call to geo.watchPosition function with high accuracy.
Old Way:
navigator.geolocation.watchPosition(geolocationSuccess,
geolocationError,
[false,true]);
New Way:
navigator.geolocation.watchPosition(geolocationSuccess,
geolocationError,
{ enableHighAccuracy: true });

To whom it might concern, it is a Google Play Services issue, reported internally here and it will be fixed from version 13.4.0
Update: solved after updating to Play Services 14.3.66, accuracy down to 4m again!

Related

What's causing Expo's ImagePicker.launchCameraAsync() to crash the app on some android devices?

I have a managed expo app that uses expo SDK 39 and expo image picker ("expo-image-picker": "~9.1.0") currently in production. Some android users have reported the camera feature "not working" which makes a call to ImagePicker.launchCameraAsync(). I have been able to reproduce it on a Nexus S Emulator running android 10, the whole app crashes. No error messages are logged.
I thought that the issue was specific to Android 10, however some users reported the issue on Android 11 (and android 8).
I tried wrapping a try-catch block to pull out an error, with no luck, the app still crashes, and no logs.
I also tried making a dummy app with only the above functionality in question, however I get the same results on the Nexus S emulator: The app crashes, no reports logged.
Here is an example of the function call which is triggering the crashes.
const takeImage = async () => {
let result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
base64: true,
});
if (!result.cancelled) {
console.log(result.uri);
}
};
The issue is discussed on github https://github.com/expo/expo/issues/7946.
It appears that the following note in the expo docs refers to the issue:
Note: Make sure that you handle MainActivity destruction on Android. See ImagePicker.getPendingResultAsync.
However, the docs seem to be describing handling a crash occuring after taking a picture. The crashing described in the question occurs immediately upon calling imagePicker's camera method.

Restoring chromecast connection fails: Unselecting the current route because it is no longer selectable

I'm developing an Android app that supports Chromecast.
When trying to reconnect, in the callback:
public void onRouteAdded(final MediaRouter router, final MediaRouter.RouteInfo route)
I check whether the routeId is the one I was connected and in that case I select it calling:
mRouter.selectRoute(newRoute)
What happens is that my onRouteSelected callback is triggered but just after that the onRouteUnselected is called. This does not happen on all the phones.
Tracing what happens I saw that the implementation of selectRoute ends sending a "route unselected" message to an Handler. The passed route is the default one so the one that was selected when I tried to select the new one. The route that I receive in the onRouteUnselected is the new one instead so the one that was just selected.
Has anyone seen this issue?
Edited
In the logs I see "Unselecting the current route because it is no longer selectable"
Following the solution suggested in this issue I installed the new version of the Google Play Services. I'm still using the jdk 1.6. The problem now is that I get onConnectionFailed with ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED.
To fix this issue with the version I had to force gradle to use version 5.0.89 that is the one that is installed on the phone. After having done this the application runs again on the phone but the reconnection still fails
Edited2
Please notice that you need to have a strong reference (mRouter) to a router object in your "ChromecastManager" class and you should use that one to select the route. You should also look in mRouter.getRoutes() for the route with newRoute.getId() == oldRoute.getId(). So you can't call mRouter.selectRoute(route) where route is the object passed to onRouteAdded
It seems it is working now. The only thing I did was to execute the router.selectRoute asynchronously and not in the onRouteAdded. So I execute the selection in an Handler.
So to update the Google Service isn't necessary. In case you update the Google Service as I did you do not need to use JDK 1.7. I don't know why I get the warning
"warning: com/google/android/gms/common/api/PendingResult.class(com/google/android/gms/com‌​mon/api:PendingResult.class): major version 51 is newer than 50, the highest major version supported by this compiler. It is recommended that the compiler be upgraded."
even if openjdk-6-jdk is already the newest version.

watchposition fires every minute on Nexus-5

Geo-location watch only fires every minute. Using a Nexus-5, android 4.4.2, and cordova 3.3.1 (I've also tried 3.4.0). It makes no difference what I define maximumAge as. The location returned is correct. In other devices I have have tested it gets fired every second. I know I can fall back on using setTimeout with getCurrentPosition, but I'd like to understand this behaviour.
navigator.geolocation.watchPosition(
function(){
console.log("success");
},
function(){
console.log("fail");
},
{
enableHighAccuracy: true,
timeout: 30000
}
);
The Geolocation Plugin is not that great, and will actually be deprecated in the next Cordova release. They suggest that you just use the HTML5 Geolocation API instead, which uses the same syntax.

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.

navigator.geolocation.getCurrentPosition fails on Android Browser

i'm trying to get the geolocation on Android Browser but nothing happens. I'm using a Samsung Galaxy S3 but i'm not sure about the version of my browser. Android version is 4.1.2
Here is my Code:
if (navigator.geolocation) {
var timeoutVal = 10 * 1000 * 1000;
navigator.geolocation.getCurrentPosition(
displayPosition,
displayError,
{ enableHighAccuracy: true, timeout: timeoutVal, maximumAge: 0 }
);
}
this is a code i copied and pasted from this site
it gives me the "navigator.geolocation"
but when it comes to "getCurrentPosition" my code stops working. Mobile Chrome works fine but this is not. I shared my position but still nothing happens. Any help will be appriciated.
Thanks.
Thanks everyone i found the solution,
i was getting the geolocation after some javascript operations. I tried to get the geolocation before document is ready. And it worked.
I know this is a bit old but it keeps coming up on searches so I thought I would add a tip that helped me.
Because I want to get the location right as the page loads I found that I needed to introduce a very short delay after the page loads. When I had no delay, I would get no error but also I would not activate the location protocols on the phone. This half second delay solved the issue. You can play with the delay and see if it solves your issues.
setTimeout(function() {getAutoLocation(true)},500);
I get the location in my "getAutoLocation(true)" function. This setTimeout only exists to introduce the delay.
seems like PhoneGap has a problem with geolocation
I have the same issue
I'm using S3 with Android 4.1.2, phonegap geolocation feature doesn't work
In order to get the geolocation without errors, you have to make that code block work before using the values provided by the geolocation because operations are carried out asynchronously, in this question i found the solution by loading my geolocation script before other .js files. This solved my problem and another trick for this issue is, geolocation works more stable when you give "always" permission for browser to read your location. After loading for the first time, you never encounter geolocation errors.
I found that some Android phones (old and new) don't run properly the function
getCurrentPosition, maybe trying to save some battery.
I played with the function watchPosition and then the high accuracy GPS kicked in.
Read this to know how to use the parameters properly:
http://dev.w3.org/geo/api/spec-source.html#watch-position
In my case, this worked:
{
maximumAge: 0, timeout: 2000, enableHighAccuracy: true
}
Hope this helps someone.
Did you give Internet permission in manifest?
<manifest xlmns:android...>
...
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

Categories

Resources