GPS service for android and suggested changes in Necessitas.

frameworks at qlands.com frameworks at qlands.com
Sun May 29 09:58:06 CEST 2011


  BODY { font-family:Arial, Helvetica, sans-serif;font-size:12px; } p
{ margin-bottom: 0.08in; }a:link {  }

	Hi,
	First of all. Thanks for the brilliant work!.... Running QT in
Android is just mind-blowing!
	I just joined the list of developers to contribute to the project.
Just after I got a copy of Necessitas 0.1 I had to see how to make
the GPS work. I wanted to implement it as most as possible with JNI
and with the minimum of java code.
  From a GPS example written in Java
(http://hejp.co.uk/android/android-gps-example/) [1] I got that the
main challenges were:
 1- To implement the LocationListener interface
 2- To connect to the location manager by
Activity.getSystemService(service)
 3- To request the locations using the Listener by
LocationManager.requestLocationUpdates(gps provider, double, float,
listener,looper)
 This main challenges arose from two main constraints:
 1- The LocationListener is an interface so needs to be implemented
in a class in a very basic way. Thus I had to include such
implementation as part of the industrius java classes: 
QtGPSListener.java with the following implementation:
 package eu.licentia.necessitas.industrius; 
  import android.location.Location; 
 import android.location.LocationListener; 
 import android.location.LocationProvider; 
 import android.os.Bundle; 
 public class QtGPSListener implements LocationListener { 
         public void onLocationChanged(Location location) 
     { 
                 // Here we get the location data and create a result

                 // string (A really basic way of passing data). We
called sndonLocationChanged that is 
                 // in fact connected to the qt application by JNI's
registeNatives 
                 String accuracy; 
                 accuracy = String.valueOf(location.getAccuracy()); 
                 String altitude; 
                 altitude = String.valueOf(location.getAltitude()); 
                 String latitude; 
                 latitude = String.valueOf(location.getLatitude()); 
                 String longitude; 
                 longitude = String.valueOf(location.getLongitude());

                 String res; 
                 res = "|AC:" + accuracy; 
                 res = res + "|AL:" + altitude; 
                 res = res + "|LA:" + latitude; 
                 res = res + "|LO:" + longitude + "|"; 
                 sndonLocationChanged(res); 
     } 
     public void onProviderDisabled(String provider)  
     { 
                 String res; 
         res = "Provider " + provider + " is disabled"; 
         sndonProviderDisabled(res); 
     } 
     public void onProviderEnabled(String provider)  
     { 
                 String res; 
         res = "Provider " + provider + " is enabled"; 
         sndonProviderEnabled(res); 
     } 
     public void onStatusChanged(String provider, int status, Bundle
extras)  
     { 
                 switch (status) { 
         case LocationProvider.OUT_OF_SERVICE: 
             sndonStatusChanged("Status Changed: Out of Service"); 
             break; 
         case LocationProvider.TEMPORARILY_UNAVAILABLE: 
             sndonStatusChanged("Status Changed: Temporarily
Unavailable"); 
             break; 
         case LocationProvider.AVAILABLE: 
             sndonStatusChanged("Status Changed: Available"); 
             break; 
                 } 
     } 
         // List of methods that will be linked in the qt application

         // using JNI's registerNatives 
         public static native void sndonLocationChanged(String
currLocation); 
         public static native void sndonProviderDisabled(String
message); 
         public static native void sndonProviderEnabled(String
message); 
         public static native void sndonStatusChanged(String
message); 
 }
 2- I had to use the activity running the qt application so I can use
 getSystemService(String). I though there were some Android API to get
an instance to the current activity running (so I can use JNI to get
it) but it seems that there is none.
 So the only alternative that I had was to pass the activity from
QtApplication.java to qtmain_android.cpp. For this I made the
following changes:
 2.1 -  In QtApplication.java: FROM: public static native void
startQtApp(String params,String env) TO: public static native void
startQtApp(String params,String env,Object currAct)
 2.2 - In qtmain_android.cpp: FROM: static jboolean
startQtApp(JNIEnv* env, jobject /*object*/, jstring paramsString,
jstring environmentString) TO: tatic jboolean startQtApp(JNIEnv* env,
jobject /*object*/, jstring paramsString, jstring
environmentString,jobject currAct)
 2.3 -  In qtmain_android.cpp: I created a jobject currActivity =
NULL that I can extern in my sample application. Plus assigning it a
global reference of the current activity:  currActivity =
env->NewGlobalRef(currAct). This to use it in JNI thread of execution

 With this changes I create a QT GPS class with JNI code that access
the GPS. I just need to pass it the JavaVM that I extern from 
qtmain_android.cpp and  currActivity.
 I can see that in 0.2 necessitas include QtLocation.java  (which
implements the GPS). Although it is fine, I reckon it is better to
implement much of that code in the c++ side with JNI and only have
the minimum java code just for implementing interfaces for example.
 This is an snapshot of the QT GPS class where it connects to the
service
 midGetSystemService =
currEnv->GetMethodID(actClass,"getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
     if (currEnv->ExceptionOccurred())
     {
         //We need to handle errors a bit better! --TODO
         currEnv->ExceptionClear();
         classError = true;
         emit error("JNI--Cannot get method getSystemService()");
         stopGPSService();
         return false; //Return with error
     }
     if (midGetSystemService == NULL)
     {
         classError = true;
         emit error("JNI--getSystemService is null");
         stopGPSService();
         return false; //Return with error
     }
     //We get a java String from a normal c++ string
     jstring StringArg = currEnv->NewStringUTF("location");
     if (currEnv->ExceptionOccurred())
     {
         classError = true;
         emit error("JNI--Error parsing string!");
         stopGPSService();
         return false; //Return with error
     }
     //We get the system service
     jSystemServiceObj =
currEnv->CallObjectMethod(currAct,midGetSystemService,StringArg);
     if (currEnv->ExceptionOccurred())
     {
         classError = true;
         emit error("JNI--Error at getting the System service");
         stopGPSService();
         return false; //Return with error
     }
     //If the service object is not null we continue
     if (jSystemServiceObj == NULL)
     {
         classError = true;
         emit error("JNI--jSystemServiceObj is null");
         stopGPSService();
         return false; //Return with error
     }
     //Now that we have the system service we need to create a new
Location Listener object.
     // Gets the constructor of the Location Listener class. Please
note that
     // listenerClass comes from the modified qtmain_android.cpp
     // It will not work with the orignal qtmain_android.cpp from
android-lighthouse
    //In a better implementation like a mobility plugin we can get
the class using Q_DECL_EXPORT JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
     midConstListener = currEnv->GetMethodID(listenerClass, "",
"()V");
     if (currEnv->ExceptionOccurred())
     {
         classError = true;
         emit error("JNI--Error registering Listener constructor");
         stopGPSService();
         return false; //Return with error
     }
     //If the method id of the constructor is not null we continue
     if (midConstListener == NULL)
     {
         classError = true;
         emit error("JNI--midConstListener is null");
         stopGPSService();
         return false; //Return with error
     }
     //Creates a new listener class
     jListenerObj = currEnv->NewObject(listenerClass,
midConstListener);
     if (currEnv->ExceptionOccurred())
     {
         classError = true;
         emit error("JNI--Error creating new listener object");
         stopGPSService();
         return false; //Return with error
     }
     //If the listener class is not null we continue
     if (jListenerObj == NULL)
     {
         classError = true;
         emit error("JNI--jListenerObj is null");
         stopGPSService();
         return false; //Return with error
     }
     //  Now that we have the new instance of the listener object
     //  We need to connect its callbacks to our methods
     //Declaring the methods. This will be used by registerNatives
     JNINativeMethod methods[] =
     {
         {"sndonLocationChanged", "(Ljava/lang/String;)V", (void
*)&androidGPS::LocationChanged},
         {"sndonProviderDisabled", "(Ljava/lang/String;)V", (void
*)&androidGPS::ProviderDisabled},
         {"sndonProviderEnabled", "(Ljava/lang/String;)V", (void
*)&androidGPS::ProviderEnabled},
         {"sndonStatusChanged", "(Ljava/lang/String;)V", (void
*)&androidGPS::StatusChanged}
     };
     //Register the native methods.
     // Basically this joins the methods inside the gpsListener.java
class
     // with our c++ methods.
     int numMethods;
     numMethods = sizeof(methods) / sizeof(methods[0]);
     if (currEnv->RegisterNatives(listenerClass, methods, numMethods)
< 0)
     {
         if (currEnv->ExceptionOccurred())
         {
             classError = true;
             emit error("JNI--Error running RegisterNatives");
             stopGPSService();
             return false; //Return with error
         }
         else
         {
             emit error("JNI--Error running RegisterNatives");
             stopGPSService();
             return false; //Return with error
         }
     }
     // Now that we have connected the methos is time to call
     // requestLocationUpdates using the jSystemServiceObj
     //Get the method id of requestLocationUpdates
     midRequestLocationUpdates =
currEnv->GetMethodID(locManClass,"requestLocationUpdates","(Ljava/lang/String;JFLandroid/location/LocationListener;Landroid/os/Looper;)V");
     if (currEnv->ExceptionOccurred())
     {
         //We need to handle errors a bit better! --TODO
         currEnv->ExceptionClear();
         classError = true;
         emit error("JNI--Cannot get method
midRequestLocationUpdates()");
         stopGPSService();
         return false; //Return with error
     }
     //If the method is not null we continue
     if (midRequestLocationUpdates == NULL)
     {
         classError = true;
         emit error("JNI--midRequestLocationUpdates is null");
         stopGPSService();
         return false; //Return with error
     }
     //Finally we call the Request updates
     qDebug() NewStringUTF("gps");
     // Call RequestLocationUpdates of the System Service object
     // (jlong)20000 means that it will request location updated
every 20 seconds
     // (jfloat)10 is the minimum distance interval for
notifications, in meters
     // Note that we pass the listener object that we just created
jListenerObj
     // and the looper that comes from the modified
qtmain_android.cpp

	   // we can pass such values as user defined!!!!
	   
currEnv->CallVoidMethod(jSystemServiceObj,midRequestLocationUpdates,StringArg,(jlong)20000,(jfloat)10,jListenerObj,currLoop);
     if (currEnv->ExceptionOccurred())
     {
         classError = true;
         emit error("JNI--Error calling midRequestLocationUpdates");
         stopGPSService();
         return false; //Return with error
     }
     //Ready... As it all bound the class the GPS server will update
the location
     // every 20 seconds using the gpsListener. As the methods of the
gpsListener
     // are bound to the c++ aplication by registerNatives we can
have the readings. 
 To conclude. A JNI implementation of the Android GPS service with
very basic java code will require to have a Jobject pointing to the
current activity running the QT application. This I reckon would make
a Mobility plugin less dependent on external java coding. But where it
would be the best place to implement this change? I made it on
qtmain_android.cpp and qtApplication.java but for a  Mobility plugin
this might not be the case.
 I register myself on gitorious. For contributing do I need to work
on a clone of master?
	Thanks a lot for the brilliant work!


Links:
------
[1] http://hejp.co.uk/android/android-gps-example/)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.kde.org/pipermail/necessitas-devel/attachments/20110529/fb9efe04/attachment-0001.htm 


More information about the Necessitas-devel mailing list