GPS service for android and suggested changes in Necessitas.

BogDan bog_dan_ro at yahoo.com
Tue May 31 11:14:06 CEST 2011


Hi,

   I just come from a long trip, I'll take a look to your mail today.


Thank you !

BogDan.


>________________________________
>From: "frameworks at qlands.com" <frameworks at qlands.com>
>To: necessitas-devel at kde.org
>Sent: Sunday, May 29, 2011 3:58 PM
>Subject: GPS service for android and suggested changes in Necessitas.
>
>
> 
>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/) 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, "<init>", "()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() << "Final call!!!";
>
>    //Convert a c++ string into jstring
>    StringArg = currEnv->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!
>
>
>
>_______________________________________________
>Necessitas-devel mailing list
>Necessitas-devel at kde.org
>https://mail.kde.org/mailman/listinfo/necessitas-devel
>
>
>


More information about the Necessitas-devel mailing list