New Google Play services stuff like low power location

warwound

Expert
Licensed User
Longtime User
Here's a basic example of detecting the device location using LocationClient, LocationRequest and LocationListener.
An Activity:

B4X:
Sub Process_Globals
   Dim LastLocation As Location
   Dim TrackingEnabled As Boolean=False
End Sub

Sub Globals
   Dim AltitudeLabel As Label
   Dim BearingLabel As Label
   Dim EnableToggle As ToggleButton
   Dim LatLngLabel As Label
   Dim SpeedLabel As Label
   Dim TimeLabel As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("Main")

   EnableToggle.Checked=TrackingEnabled

   If LastLocation.IsInitialized Then
      LocationChanged
   End If
   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub EnableToggle_CheckedChange(Checked As Boolean)
   TrackingEnabled=Checked
   If TrackingEnabled Then
      StartService(LocationTracker)
   Else
      StopService(LocationTracker)
   End If
End Sub

Sub LocationChanged
   AltitudeLabel.Text=LastLocation.Altitude
   BearingLabel.Text=LastLocation.Bearing
   LatLngLabel.Text=LastLocation.Latitude&", "&LastLocation.Longitude
   SpeedLabel.Text=LastLocation.Speed
   TimeLabel.Text=DateTime.Time(LastLocation.Time)
End Sub

And a Service:

B4X:
Sub Process_Globals

   Dim GooglePlayServicesHelper1 As GooglePlayServicesHelper
   Dim LocationClient1 As LocationClient
   Dim LocationListener1 As LocationListener
   Dim LocationRequest1 As LocationRequest

End Sub

Sub Service_Create
   LocationClient1.Initialize("LocationClient1")

End Sub

Sub Service_Start (StartingIntent As Intent)

   If LocationClient1.IsConnected Then
      Log("LocationClient IsConnected")
   Else
      Dim ServiceStatus As Int=GooglePlayServicesHelper1.IsGooglePlayServicesAvailable
      Select ServiceStatus
         Case GooglePlayServicesHelper1.SUCCESS
            Log("GooglePlayServicesHelper IsGooglePlayServicesAvailable service available")
            Log("LocationClient Connecting")
            LocationClient1.Connect
         Case Else
            Log("GooglePlayServicesHelper IsGooglePlayServicesAvailable returned: "&ServiceStatus)
            Log("Google Play Services unavailable")
            StopService("")
      End Select
   End If
   
End Sub

Sub Service_Destroy

   If LocationRequest1.IsInitialized Then
      LocationClient1.RemoveLocationUpdates(LocationListener1)
   End If
   
   If LocationClient1.IsConnected Then
      LocationClient1.Disconnect
   End If
   
End Sub

Sub LocationClient1_Connected
   Log("LocationClient1_Connected")
   
   '   the GPS library is required as we are using the Location object
   '   check to see if a previous last location exists
   '   if it does then pass it to our Sub that handles the LocationListener LocationChanged event
   '   (force a location changed event before the LocationClient gets it's next GPS fix) 
   Dim LastLocation As Location=LocationClient1.GetLastLocation
   If LastLocation.IsInitialized Then
      Log("LastLocation being passed to LocationListener1_LocationChanged")
      LocationListener1_LocationChanged(LastLocation)
   End If

   LocationRequest1.Initialize
   
   '   detailed info on the LocationRequest parameters can be found:
   '   http://developer.android.com/training/location/receive-location-updates.html
   '   http://developer.android.com/reference/com/google/android/gms/location/LocationRequest.html
   
   LocationRequest1.SetFastestInterval(1000*1)         '   think of this as the fastest updates that your event handling Sub can handle
   LocationRequest1.SetInterval(1000*6)            '   this is the desired frequency of location update (units of milliseconds)
   LocationRequest1.SetNumUpdates(9999)            '   the maximum number of updates that should be reported, after this number no more will be reported
   LocationRequest1.SetPriority(LocationRequest1.PRIORITY_HIGH_ACCURACY)   '   the desired accuracy/power usage mode
   
   LocationListener1.Initialize("LocationListener1")
   
   LocationClient1.RequestLocationUpdates(LocationRequest1, LocationListener1)
   
   
End Sub

Sub LocationClient1_ConnectionFailed(ErrorCode As Int)
   Log("LocationClient1_ConnectionFailed ErrorCode="&ErrorCode)
   StopService("")
End Sub

Sub LocationClient1_Disconnected
   Log("LocationClient1_Disconnected")
   StopService("")
End Sub

Sub LocationListener1_LocationChanged (Location1 As Location)
   Main.LastLocation=Location1
   CallSub(Main, "LocationChanged")
End Sub

So in the Activity the user can use the ToggleButton to start and stop the Service.
The Service, when running, listens for location updates.
The Activity has a Process Global Location object that is initially not initialized.
When the Service gets a location update it sets the location update as the Activity Process Global Location object and calls a Sub in the Activity to update the Activity layout.
If the service is running and the user exits the app then the service will continue to run and update the Process Global, if the Activity is resumed it will update it's layout with the values of the last reported location.
All pretty straightforward stuff!

Lines 70 to 73 of the Service define the accuracy and frequency of the location requests and therefore the amount of device battery power that will be used.
It's listening for maximum accuracy location changes every 6000 milliseconds.
After 9999 location updates have bene reported presumably the LocationClient will report no more.

Compile the code and look in the manifest.xml file, you'll see:

B4X:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

This permission is currently hardcoded into the library.
You can use the Location API with either android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION.
If that permission in the manifest was ACCESS_COARSE_LOCATION instead of ACCESS_FINE_LOCATION then:

Sometimes it will be desirable/preferable to use android.permission.ACCESS_COARSE_LOCATION instead of android.permission.ACCESS_FINE_LOCATION.

Question: Shall i therefore remove the hardcoded android.permission.ACCESS_FINE_LOCATION from the library and leave the b4a developer to manually add the permission they require to the manifest file?
Or i believe i could leave the android.permission.ACCESS_FINE_LOCATION hardcoded and if a b4a developer wants to instead use android.permission.ACCESS_COARSE_LOCATION there are manifest editor commands that can be used to remove the unwanted permission and add the wanted permission.

Neither solution is perfect - the b4a developer will be required to manually manage their manifest permissions.

LocationClient, LocationRequest and LocationListener work well and can be considered to be ready for use in your b4a apps.

Be sure to check out both links to the android documentation in the code to read about the various LocationRequest parameters.
The FastestUpdate parameter is particularly interesting - if another app on the device is getting location updates then these location updates can also be passed to your app. The FastestInterval parameter tells android how fast your code can handle location updates so that it doesn't report updates (raise events) faster than your code can handle.

Demo project attached.

Martin.

(PS Please do comment on the permissions and how you think it is best to handle them).
 

Attachments

  • LocationClient.zip
    8.7 KB · Views: 542
Upvote 0

Harris

Expert
Licensed User
Longtime User
Shall i therefore remove the hardcoded android.permission.ACCESS_FINE_LOCATION from the library and leave the b4a developer to manually add the permission they require to the manifest file?
Or i believe i could leave the android.permission.ACCESS_FINE_LOCATION hardcoded and if a b4a developer wants to instead use android.permission.ACCESS_COARSE_LOCATION there are manifest editor commands that can be used to remove the unwanted permission and add the wanted permission.

It is too bad that coarse or fine cant be set within the app.
If the device is constantly powered (as mounted in a powered vehicle cradle), then fine is great. Otherwise, coarse is needed to save battery. You never know how the app will be used - in my case....

Thanks for your wonderful work.
 
Upvote 0

marcel

Active Member
Licensed User
Longtime User
Demo project attached.

B4X:
B4A line: 77
LocationClient1.RequestLocationUpdates(LocationRequest1, LocationListener1)
javac 1.6.0_26
src\uk\co\martinpearman\b4a\locationapidemo\locationtracker.java:118: package com.google.android.gms.location does not exist
_locationclient1.RequestLocationUpdates((com.google.android.gms.location.LocationRequest)(_locationrequest1.getObject()),(com.google.android.gms.location.LocationListener)(_locationlistener1.getObject()));
                                                                        ^
1 error

I think I am missing some files?
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
I think I am missing some files?

Probably missing the latest version of google-play-services.jar.
See my post here: http://www.b4x.com/forum/basic4andr...stuff-like-low-power-location.html#post172587

You will need a copy of the latest version of google-play-services.jar in your b4a additional libraries folder - that .jar file is 578KBs in size so i couldn't include it in the attached files.
Setup Google Play Services SDK | Android Developers.

Martin.
 
Upvote 0

marcel

Active Member
Licensed User
Longtime User
Hi Martin,

I did leave tonight the test program running until this morning the battery was complete empty. I expected that it will only use the Cell Tower or last Know position becuase it was not moved at all.

When I read at the documentation, I am not sure I get it. Or I did read the wrong one :)

When does it use Cell Tower position and when does it switch to GPS? Is this something you need to do yourself or is this automaticly?

And what is the difference between the LocationManager and this?

Can you help me a little bit to keep on track. Currently I am using only the GPS module in the app but I got lot of complains because of the battery drain.
 
Upvote 0

basil99

Active Member
Licensed User
Longtime User
Hi, warwound

thanks for this lib, here some test results:

DEVICE: No SIM Card, GPS build in
OS: 2.2
NETWORK: WiFi connection
BLUETOOTH: Connected to the PC


works fine and fast.

Howerer, I compare the results with Erel's GPS sample http://www.b4x.com/forum/basic4android-getting-started-tutorials/6592-gps-tutorial.html#post38535. Your lib is much faster, i get location almost immediatly. GPS sample sometimes requires several minutes to display Lat/Lng. But i'm confused with some difference in results ( using Location.ConvertToSeconds ):

Erel GPS sample:
Lat: MM:SS:1.57
Lng: MM:SS:20.83

values are stable

Yor lib:
Lat: MM:SS:2.41 - 2.42
Lng: MM:SS:20.84 - 20.99

values are changing permanently, never mind phone is not moved at all, just on it place on the table

my question is - what results are more accurate from your point of view ?
 
Last edited:
Upvote 0

jmon

Well-Known Member
Licensed User
Longtime User
Device: HTC Desire
OS: Android 2.2, Rooted
WIFI: OFF


-->> I don't get anything working. The application launches, but no location, speed, altitude ... is found. Even "Time" isn't displayed, so it means the event LocationChanged isn't triggered.

WIFI: ON
-->> Ok, "lat,lng" works with wifi ON, but no altitude, speed or bearing


So what is it? the app cannot retrieve my location from the towers?

Also, regarding the permissions, I vote for the way it is now, in version 0.05, with both permissions forced, because the reality of this library is that it retrieves a very accurate location.
 
Last edited:
Upvote 0

netchicken

Active Member
Licensed User
Longtime User
Working with Location i find that with GPS off you get the GPS lat Lon, but thats all.

With GPS on you get the other location properties, speed, altitude etc.

Also the update time is much faster with GPS on.
 
Last edited:
Upvote 0

jmon

Well-Known Member
Licensed User
Longtime User
Device: HTC Desire
OS: Android 2.2, Rooted
WIFI: OFF


-->> I don't get anything working. The application launches, but no location, speed, altitude ... is found. Even "Time" isn't displayed, so it means the event LocationChanged isn't triggered.

WIFI: ON
-->> Ok, "lat,lng" works with wifi ON, but no altitude, speed or bearing


So what is it? the app cannot retrieve my location from the towers?

Also, regarding the permissions, I vote for the way it is now, in version 0.05, with both permissions forced, because the reality of this library is that it retrieves a very accurate location.
After doing more tests, I found that the tower reception is very random. It only works in 80% of my tests. I feel that it doesn't work properly in interior.
 
Upvote 0

marcel

Active Member
Licensed User
Longtime User
Hi,

I drove arround with the test program but after a while it stoped working. I thought it might be the `SetNumUpdates` so I restarted the program but still had some trouble. I restarted the phone and it worked again.

But also sometime without restarting I see the time of the update changed again.

I have an Samsung Android S2 4.1.2
 
Upvote 0

corwin42

Expert
Licensed User
Longtime User
@warwound
Are you still working on this library or is the version you posted a month ago the final version?
Will you open a new thread for it in the Libraries forum?
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
@warwound
Are you still working on this library or is the version you posted a month ago the final version?
Will you open a new thread for it in the Libraries forum?

I think thats a good idea - this thread has gone a bit stale, i've been too busy to keep track of the questions and help with replies.

I'll get a new thread started a bit later today.

Martin.
 
Upvote 0

corwin42

Expert
Licensed User
Longtime User
I haven't seen a new thread in libraries forum until now. Anyway I will start to use this library now in my projects.

Thanks very much warwound.
Good work!
 
Upvote 0

marcel

Active Member
Licensed User
Longtime User
Hi,

I would like to user the library. In my test program it works fine. But when I convert it to my app I get an error:

B4X:
GooglePlayServicesHelper IsGooglePlayServicesAvailable service available
LocationClient Connecting
LocationClient1_Connected
 
 
java.lang.IllegalStateException: Not connected. Call connect() and wait for onConnected() to be called.
 
 
    at com.google.android.gms.internal.p.n(Unknown Source)
    at com.google.android.gms.internal.ce.a(Unknown Source)
    at com.google.android.gms.internal.ce$c.n(Unknown Source)
    at com.google.android.gms.internal.cd.getLastLocation(Unknown Source)
    at com.google.android.gms.internal.ce.getLastLocation(Unknown Source)
    at com.google.android.gms.location.LocationClient.getLastLocation(Unknown Source)
    at uk.co.martinpearman.b4a.android.gms.location.LocationClient.GetLastLocation(LocationClient.java:107)
    at com.yazula.android.update._locationclient1_connected(update.java:149)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:173)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:157)
    at uk.co.martinpearman.b4a.android.gms.location.subclasses.LocationClient$1.onConnected(LocationClient.java:23)
    at com.google.android.gms.internal.p.k(Unknown Source)
    at com.google.android.gms.internal.p$f.a(Unknown Source)
    at com.google.android.gms.internal.p$f.a(Unknown Source)
    at com.google.android.gms.internal.p$b.p(Unknown Source)
    at com.google.android.gms.internal.p$a.handleMessage(Unknown Source)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4921)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
    at dalvik.system.NativeStart.main(Native Method)
java.lang.RuntimeException: java.lang.IllegalStateException: Not connected. Call connect() and wait for onConnected() to be called.
 
 
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:199)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:157)
    at uk.co.martinpearman.b4a.android.gms.location.subclasses.LocationClient$1.onConnected(LocationClient.java:23)
    at com.google.android.gms.internal.p.k(Unknown Source)
    at com.google.android.gms.internal.p$f.a(Unknown Source)
    at com.google.android.gms.internal.p$f.a(Unknown Source)
    at com.google.android.gms.internal.p$b.p(Unknown Source)
    at com.google.android.gms.internal.p$a.handleMessage(Unknown Source)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4921)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: Not connected. Call connect() and wait for onConnected() to be called.
    at com.google.android.gms.internal.p.n(Unknown Source)
    at com.google.android.gms.internal.ce.a(Unknown Source)
    at com.google.android.gms.internal.ce$c.n(Unknown Source)
    at com.google.android.gms.internal.cd.getLastLocation(Unknown Source)
    at com.google.android.gms.internal.ce.getLastLocation(Unknown Source)
    at com.google.android.gms.location.LocationClient.getLastLocation(Unknown Source)
    at uk.co.martinpearman.b4a.android.gms.location.LocationClient.GetLastLocation(LocationClient.java:107)
    at com.yazula.android.update._locationclient1_connected(update.java:149)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:173)
    ... 15 more

Could this because of timing issues?
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
java.lang.RuntimeException: java.lang.IllegalStateException: Not connected. Call connect() and wait for onConnected() to be called.

Is it connecting but then disconnecting?

Can you add a sub to listen for the LocationClient Disconnected event and see if that event is raised after the Connected event?
You might also want to add a sub to listen for the LocationManager ConnectionFailed(ErrorCode As Int) event and see if that event is raised.

Martin.
 
Upvote 0

marcel

Active Member
Licensed User
Longtime User
Thanks Martin!! I found it using your suggestions! I used
B4X:
Dim LastLocation As Location=LocationClient1.GetLastLocation
before it was even open.

Great library!!
 
Upvote 0

marcel

Active Member
Licensed User
Longtime User
From Google

To check that the APK is installed, call GooglePlayServicesUtil.isGooglePlayServicesAvailable(), which returns one of the integer result codes listed in the API reference documentation. If you encounter an error, call GooglePlayServicesUtil.getErrorDialog() to retrieve localized dialog that prompts users to take the correct action, then display the dialog in a DialogFragment. The dialog may allow the user to correct the problem, in which case Google Play services may send a result back to your activity

Could you also implement the ErrorDialog?
 
Upvote 0

corwin42

Expert
Licensed User
Longtime User
Since B4A 3.20 you can add it yourself with the #AdditionalRes attribute and JavaObject/Reflection library.

- Download the Play Services SDK with the Android SDK Manager.
- Copy the ?\android-sdk\extras\google\google_play_services\libproject\google-play-services_lib\res to some folder
- In your B4A Project add this folder with the #AdditionalRes attribute.

B4X:
    #AdditionalRes: <path_to_the_folder>, com.google.android.gms

In Activity_Resume write the following:

B4X:
Sub Activity_Resume
    Dim gpsHelper As GooglePlayServicesHelper
    Dim errorCode As Int
   
    errorCode = gpsHelper.IsGooglePlayServicesAvailable
    If errorCode <> gpsHelper.SUCCESS Then
        Log("Play Services not available -> try to install")
   
        Dim jo As JavaObject
        Dim r As Reflector
       
        jo.InitializeStatic("com.google.android.gms.common.GooglePlayServicesUtil")
        If jo.RunMethod("isUserRecoverableError", Array As Object(errorCode)) Then
            jo.RunMethodJO("getErrorDialog", Array As Object(errorCode, r.GetActivity, 9999)).RunMethod("show", Null)
        Else
            Log("Play Services not available for this device")
            'Show error message here
        End If
    Else
        Log("Play Services available")
    End If
End Sub
 
Upvote 0
Top