Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

getCurrentLocation gets timeout on Android API17 #42

Closed
dxshindeo opened this issue Dec 13, 2016 · 24 comments · Fixed by #72
Closed

getCurrentLocation gets timeout on Android API17 #42

dxshindeo opened this issue Dec 13, 2016 · 24 comments · Fixed by #72
Assignees
Labels
Milestone

Comments

@dxshindeo
Copy link

dxshindeo commented Dec 13, 2016

My app works fine on Android API 22 and higher. Got my hands on phone with API 17. Made a fresh tns create test --ng project, with "nativescript-geolocation": "^0.0.17" version. Every request that gets sent, gets timed out.

    ngAfterViewInit() {
        console.log("View initialized...");
        geolocation.getCurrentLocation({
            desiredAccuracy: 3, 
            updateDistance: 1, 
            maximumAge: 20000, 
            timeout: 20000
        }).then(function(loc) {
            console.log("LOC:");
            console.log(JSON.stringify(loc, null, 4));
        }).catch(function(response){
            console.log("E: ", response);
        });
   }
@nsndeck
Copy link
Contributor

nsndeck commented Dec 23, 2016

Hi @dxshindeo Unfortunately I cannot find a device with API 17. I've tested it on API 18 and everything works as expected. Could you elaborate a little bit more info about this device? Do other apps (involving location services) work properly?

@bart
Copy link

bart commented Jan 4, 2017

I have this issue on Android 6 API 23. First I thought it's a Genymotion issue but I installed it on my Honor 6 where I'm getting a timeout when hitting the button. Any idea?

@dxshindeo
Copy link
Author

dxshindeo commented Jan 4, 2017

I couldn't get this to work, so I switched to using Google's FusedLocationAPI (nativescript-geolocation uses android's built in LocationManager).

I have an idea though (but won't have time to test it). First stating 2 facts:

  • when I was getting my location with nativescript-geolocation, it started working after I ran gmaps. It can be run either by running nativescript-google-maps-sdk plugin and running its setMyLocationEnabled(true) function OR minimizing current app and opening up Google Maps app (have to press to "accept" if agreement dialog pops up, also, have to allow your phone to use google location services).
  • when I am running another app in emulator and getting location with Google's FusedLocationAPI, it will always give me null, unless I minimize that app and open up Google Maps app.

In both scenarious, I think there is some map data flush happening that kickstarts the whole shabang. If I remember correctly, there should be some google maps flush function available in its sdk, but I'll be able to check that when I have more free time on my hands.

@dxshindeo
Copy link
Author

dxshindeo commented Jan 4, 2017

Well, I stand corrected (https://guides.codepath.com/android/Retrieving-Location-with-LocationServices-API):

Are you getting null when calling LocationServices.FusedLocationApi.getLastLocation? This is normal since this method only returns if there is already a location recently retrieved by another application. If this returns null, this means you need start receiving location updates with LocationServices.FusedLocationApi.requestLocationUpdates before receiving the location as shown above.

Which means I got null, because no other app had gotten location data. And when I started Google Maps app, it simply requested its updates.

Weird though, I don't want to initialize cycle for my locations data retrieval. I just want to get it once, which means i have to start the update cycle and upon first data retrieval, end it.

@bart
Copy link

bart commented Jan 4, 2017

Thanks a lot for sharing your ideas and experience. Good to know, that other people are facing the exact same issue. What do you think? Is it possible to fix this inside of the nativescript geolocation package? In my opinion this would be the best way because the two facts from above just sound like workarounds.

@dxshindeo
Copy link
Author

dxshindeo commented Jan 5, 2017

I suggest you actually switch to Googles' FusedLocationApi. Even android itself suggests us to do that (https://developer.android.com/reference/android/location/package-summary.html):

This API is not the recommended method for accessing Android location.
The Google Location Services API, part of Google Play services, is the preferred way to add location-awareness to your app. It offers a simpler API, higher accuracy, low-power geofencing, and more. If you are currently using the android.location API, you are strongly encouraged to switch to the Google Location Services API as soon as possible.

@bart
Copy link

bart commented Jan 7, 2017

I will give it a try, thanks!

@pietrovismara
Copy link

pietrovismara commented Jan 28, 2017

@bart @dxshindeo Same problem here, i'm stuck with development since app won't work on both Android 6 and 7.
Can anyone point me on how to access FusedLocationApi with nativescript?

@bart
Copy link

bart commented Jan 30, 2017

I have the same problem here. It still does not work. Any solutions?

@pietrovismara
Copy link

@bart I'm stuck since days, i'm thinking to switch to pure android or something like react-native. There is not a single information on the web on how to access the FusedLocationApi from nativescript.

Nativescript should be production ready, but if these are the response time on blocking issues, it's clearly not suited for production.

@dxshindeo
Copy link
Author

It is not easy, but doable. I've stepped into many pitfalls, but I got it working after much trial and error. When I get home today, I will share my code here on how to access FusedLocationApi (~8h).

@bart
Copy link

bart commented Jan 30, 2017

Sounds great and looking forward to your solution @dxshindeo ! Thanks

@dxshindeo
Copy link
Author

I will presume that you have installed nativescript-google-maps-sdk plugin and setup it till point where you get mapReady callback, otherwise look it up and start with that.
The problem with this plugin is that its not really documented on how to override/extend functions baked in its java code (getting used to doing that is another painfull story).
So here are the main things you need to know how to make gmaps show and update your location correctly:

  1. To use FusedLocationApi, we need to import its library in our app.gradle file as follows:
dependencies {
    compile 'com.google.android.gms:play-services-location:+'
}
  1. Since we are going to extend gmaps base classes/methods, we will need to access them. At the top of your .ts file, add declare var com:any;, otherwise you will get cannot find name error.
  2. First thing you will need to do, is initialize the Google Api Client in order to use FusedLocationApi. Once it is connected, start rolling in your location data.
	// global vars
	GoogleApiClient = com.google.android.gms.common.api.GoogleApiClient;
	LocationServices = com.google.android.gms.location.LocationServices;
	LocationRequest = com.google.android.gms.location.LocationRequest;
	LocationListener = com.google.android.gms.location.LocationListener;
	LocationSettingsRequest = com.google.android.gms.location.LocationSettingsRequest;
	LocationSettingsResult = com.google.android.gms.location.LocationSettingsResult;
	Maps = com.google.android.gms.maps;
	googleApiClient = null;
	my_last_known_location = {
		latitude: null,
		longitude: null
	};
	locationRequest;
	blueDotLocationListener;

	initGoogleApiClient() {
		// reference: https://www.snip2code.com/Snippet/1032941/NativeScript-Android-location-service-ba , https://gist.github.com/naderio/0fa72e58660827abf3ee
		var dis = this;

		return new Promise(function(resolve, reject){
			// creat location request with default values
			dis.locationRequest = new dis.LocationRequest.create();
			dis.locationRequest.setInterval(10000);
			dis.locationRequest.setFastestInterval(10000);

			if (dis.googleApiClient == null) {
				dis.googleApiClient = new dis.GoogleApiClient.Builder(app.android.context)
				.addConnectionCallbacks(new dis.GoogleApiClient.ConnectionCallbacks({
					onConnected: function() {
						//console.log("GoogleApiClient: CONNECTED");

						dis.LocationServices.FusedLocationApi.requestLocationUpdates(dis.googleApiClient, dis.locationRequest, new dis.LocationListener({
							onLocationChanged: function(data) {
								// update blue dot location only if mapReady is done (because you might want to get your location rolling before maps start, for exmaple, connecting to a server closest to you)
								if ( dis.blueDotLocationListener ) {
									dis.blueDotLocationListener.onLocationChanged(data);
								}

								var location = dis.serialize(data);

								if (!location) {
									return;
								}

								dis.my_last_known_location = {
									latitude: location.latitude,
									longitude: location.longitude
								}

								resolve(true);
							}
						}));
					},
					onConnectionSuspended: function() {
						//console.log("GoogleApiClient: SUSPENDED");
					}
				}))
				.addOnConnectionFailedListener(new dis.GoogleApiClient.OnConnectionFailedListener({
					onConnectionFailed: function() {
						//console.log("GoogleApiClient: CONNECTION ERROR");
					}
				}))
				.addApi(dis.LocationServices.API)
				.build();
			}

			dis.googleApiClient.connect();
		});
	}

	getLastKnownLocation() {
		var dis = this;
		return new Promise(function(resolve, reject) {
			var location = dis.LocationServices.FusedLocationApi.getLastLocation(dis.googleApiClient);
			resolve(dis.serialize(location));
		});
	}

	serialize(location) {
		//console.log("T", location);
		return location ? {
			provider: location.getProvider(),
			timestamp: new Date(location.getTime()),
			accuracy: location.hasAccuracy() ? location.getAccuracy() : null,
			latitude: location.getLatitude(),
			longitude: location.getLongitude(),
			altitude: location.hasAltitude() ? location.getAltitude() : null,
			speed: location.hasSpeed() ? location.getSpeed() : null,
			bearing: location.hasBearing() ? location.getBearing() : null,
			extras: location.getExtras(),
		} : null;
	}

  1. When the gmaps is ready, initialize the following:

	mapReady(args) {
		//console.log("MAP READY");

		var dis = this;
		// set global var
		dis.map_args = args;

		var gMap = dis.map_args.gMap;
		// By default, GoogleMap uses its own location provider, which is not the Fused Location Provider. 
		// In order to use the Fused Location Provider (which allows you to control the location accuracy and power consumption) 
		// you need to explicitely set the map location source with GoogleMap.setLocationSource()
		gMap.setLocationSource( new com.google.android.gms.maps.LocationSource({
			activate: function(onLocationChangedListener){
				dis.blueDotLocationListener = onLocationChangedListener;
			}
		}) );

		gMap.setMyLocationEnabled(true);

		var ui_interface = gMap.getUiSettings();
		ui_interface.setMyLocationButtonEnabled(false);
	}

Explanation:
gMap.setMyLocationEnabled(true); <-- this part enables the blue dot (my location) layer.
gMap.setLocationSource - read the commented part.
setMyLocationButtonEnabled(false) - simply hiding the "find me" button, because I am using my own button with the same functionality.

  1. getLastKnownLocation function will only return results if an app on your device has asked for location data recently. In other words, either we start requesting location updates first OR we minimize app and lets say open google maps app.

I believe this should get the ball rolling. This certainly is 90% more what I had to go on when I started this >.>

@bart
Copy link

bart commented Jan 31, 2017

Thank you so much @dxshindeo for sharing this. It looks like it's only an android solution. The main idea behind nativescript is its cross platform compatibility. Do you have an idea for that? My main question here is: Why does the nativescript-geolocation plugin not work correctly on all devices?

@dxshindeo
Copy link
Author

dxshindeo commented Jan 31, 2017

Yes, this code fragment is for android only. I haven't gotten around to make iOS version yet, which will happen only after few months.

You are right about the main idea about nativescript. So why aren't the necessary functions which fuel FusedLocationAPI been extracted and wrapped in nativescript-google-maps-sdk plugin? Simple. People who wrote that plugin either forgot/didn't notice/didn't care about it at that time. If you wanna push that stone, you will have to do it with those people.

Why does the nativescript-geolocation plugin not work correctly on all devices?

If you read up, even devs can't figure it out yet. And I doubt they will have time to push this, since FusedLocationApi is much better than built in android LocationManager.

@nmongiya
Copy link

is there any timeline on this issue yet?

@DimitarTachev
Copy link
Contributor

As there are a lot of issues with the Android Location Manager and it's not the recommended approach for getting a location on Android, we fully replaced the Android implementation with the Google Play Location Service one (used by Google Maps).

The change is currently on a pull request and we are still testing some edge cases. However, you could check if your issues are resolved using the tachev/location-manager-to-google-play-services branch.

@ivanblazevic
Copy link

@DimitarTachev what is a proper way to run the demo from PR? When I switch to demo directory and execute tns run android I keep getting Failed to find module: "nativescript-geolocation", relative to: app/tns_modules/ in simulator.

@DimitarTachev
Copy link
Contributor

@ivanblazevic, I suppose you miss 'npm run tsc' in the src folder before running the demo app.

@ivanblazevic
Copy link

npm run build fixed the problem, also I had to add nativescript-permissions plugin, looks like it is not included in npm config of demo. Now I'm able to run the demo but need to figure out why I'm getting "update play services" dialog after I click on allow location:

https://dl.dropboxusercontent.com/content_link/poRb3v4hbvAwxRbIWcaABcu9w0wR7wrupEbwLEvaN5CtbjeWmYeCdxj93sWW35X4/file

I'm running android 7.1.1 version, btw when I click on update button nothing happens...

@DimitarTachev
Copy link
Contributor

DimitarTachev commented Oct 9, 2017

@ivanblazevic Is the update button click crashing on a real device? The plugin requires Google Play Services 11.4.0+. The update button is part of the Google Play Services SDK and should navigate you to the Play Store. If you use emulators, you could take a look at this thread. I was able to update my Play Services version using the Android Studio 3.0 emulators. The button is not working in emulators without Play Store.

@zbranzov
Copy link
Contributor

zbranzov commented Oct 9, 2017

I couldn't find api 17 Android device so I tested on 5.0.2 Lenovo A6010 and Android 5.0 Lenovo Vibe X2. Both devices were receiving locations.

@DimitarTachev
Copy link
Contributor

We closed the issues as the Android implementation is fully replaced by a new one and we are not able to reproduce the timeouts anymore.

You could take a look at the 4.0.0 release notes here.

@ivanblazevic
Copy link

@DimitarTachev problem was with emulator, I updated Google Play Services using latest 3.0 emulators but after the update I was getting:

10-11 12:26:53.568 2190 2233 W zygote : Couldn't lock the profile file /data/user/0/com.google.android.googlequicksearchbox/files/velour/verified_jars/JS1bTx6n5gskKdcs8I56Wzg_2.jar.prof: Failed to open file '/data/user/0/com.google.android.googlequicksearchbox/files/velour/verified_jars/JS1bTx6n5gskKdcs8I56Wzg_2.jar.prof': No such file or directory 10-11 12:26:53.568 2190 2233 W zygote : Could not forcefully load profile /data/user/0/com.google.android.googlequicksearchbox/files/velour/verified_jars/JS1bTx6n5gskKdcs8I56Wzg_2.jar.prof

Anyways, I'll create new project with this new version and let you know if I get any errors.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants