OpenERP expose ses services web en json-rpc, qui sont utilisés par le client web pour se connecter, récupérer le modèle des données, les données... Il est donc possible d’utiliser ce même protocole depuis Android pour se connecter et utiliser ces services.

Paramétrage du projet Android :

L'exemple ci dessous à été effectué avec l'IDE Eclipse en utilisant la plateforme 4.3 d'Android API niveau 18.

Il est possible de télécharger le projet d'exemple depuis le gestionnaire de source.

git clone https://bitbucket.org/petrus-v/openerpconnection.git

Il existe 2 branches :

  • Une branche pour la connexion http : git fetch && git checkout http-example
  • une deuxième pour tester la conexion https : git fetch && git checkout https-example

Ajouter la librairie json-rpc au projet en copiant le fichier android-json-rpc-0.3.4.jar dans le répertoire /libs/ de votre projet.

Dans le fichier manifeste /AndroidManifest.xml, ajouter l'élément suivant pour autoriser l'application à accéder à internet :

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

Connexion http :

Création de la classe asynchrone qui effectue la connexion à OpenERP, elle étend la classe AsynTask disponible dans le Framework d’Android.

/src/fr.anybox.openerpconnection/OpenERPConnection.java :

package fr.anybox.openerpconnection;  import org.alexd.jsonrpc.JSONRPCException; import org.alexd.jsonrpc.JSONRPCHttpClient; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject;  import android.content.Context; import android.os.AsyncTask; import android.util.Log;  public class OpenERPConnection extends AsyncTask{     static final String TAG = "OERP";     static final String HOST = "http://test.openerp.verkest.fr"; //won't work, this url is not set as http     static final String REQUEST_AUTHENTICATE = "/web/session/authenticate";     static final String REQUEST_SEARCH_READ = "/web/dataset/search_read";     static final String DATABASE = "oerp";     static final String USER = "demo";     static final String PASSWORD = "demo";     static final String method = "call";          @Override     protected Void doInBackground(Context... params) {         //The http client that allow to keep the session open over requests         HttpClient httpClient = new DefaultHttpClient();                  JSONRPCHttpClient jsonRPC_client = new JSONRPCHttpClient(httpClient, HOST + REQUEST_AUTHENTICATE);         jsonRPC_client.setConnectionTimeout(2000);         jsonRPC_client.setSoTimeout(2000);         jsonRPC_client.setEncoding("UTF-8");                  try {             //set params to send to OpenERP to create the connection             JSONObject jsonParams = new JSONObject();             jsonParams.put("db", DATABASE);             jsonParams.put("login", USER);             jsonParams.put("password",USER);                          //Do the connection with openERP             JSONObject json_result = jsonRPC_client.callJSONObject(method, jsonParams);             Log.d(TAG, "We are connect to OpenERP, session id: " + json_result.getString("session_id"));                  //Now we can request partner using the same session (http client)             jsonRPC_client = new JSONRPCHttpClient(httpClient, HOST + REQUEST_SEARCH_READ);             jsonRPC_client.setConnectionTimeout(2000);             jsonRPC_client.setSoTimeout(2000);             jsonRPC_client.setEncoding("UTF-8");              //get the user_context from the connection result, to send to OpenERP in the next request             JSONObject context = json_result.getJSONObject("user_context");                          //set params to send to OpenERP service             jsonParams = new JSONObject();             jsonParams.put("session_id", json_result.getString("session_id"));             jsonParams.put("context", context);             jsonParams.put("model", "res.partner");             jsonParams.put("limit",10);                          //Domain is use in openerp to define the where sql             JSONArray domain =  new JSONArray();             JSONArray customer = new JSONArray();             JSONArray person = new JSONArray();             //Get only customers             customer.put("customer");             customer.put("=");             customer.put("1");             domain.put(customer);              //Get only person (no companies)             person.put("is_company");             person.put("=");             person.put("0");             domain.put(customer);              //domain : [[customer, =, 1],[is_company, =, 0]]             //SQL likes : WHERE customer = 1 AND is_company = 0             jsonParams.put("domain",domain);                           //choose fields you want to get             JSONArray fields = new JSONArray();             fields.put("name");             fields.put("city");             jsonParams.put("fields", fields);                          //jsonParams.put("offset",0);             jsonParams.put("sort","write_date desc");                          //send the request to openerp             json_result = jsonRPC_client.callJSONObject(method, jsonParams);             JSONArray customers = json_result.getJSONArray("records");             JSONObject oerp_customer;             for(int i =0 ; i<10;i++){                 oerp_customer = customers.getJSONObject(i);                 Log.d(TAG, oerp_customer.getString("name") + " lives in " + oerp_customer.getString("city"));             }                           } catch (JSONException e) {             Log.e(TAG,"Json exception: " + e.getMessage(), e);         } catch (JSONRPCException e) {             Log.e(TAG,"Json-rpc exception: " + e.getMessage(), e);         }         return null;     }  }

Un bouton a été ajouté dans la définition de la vue de l'activité principale :

/res/layout/activity_main.xml :

... <Button     android:id="@+id/do_connection"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:text="@string/do_connection" /> ...

Enfin, dans la méthode onCreate de l’activité, l’événement onClick du bouton est redéfini.

Fichier /src/fr.anybox.openerpconnection/MainActivity.java :

...     OpenERPConnection oc = new OpenERPConnection();           @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         // get the button and set it's listener         Button connect = (Button)findViewById(R.id.do_connection);         OnClickListener l = new OnClickListener() {                          @Override             public void onClick(View v) {                 if(oc.getStatus()==Status.FINISHED){                     //if AsyncTask is done, we have to create a new instance                     oc = new OpenERPConnection();                 }                 if(oc.getStatus()== Status.PENDING){                     // AsyncTask is ready to run                     oc.execute(getApplicationContext());                 }else if(oc.getStatus()== Status.PENDING) {                     //if AsynTask is already running, just wait and click again                     Toast.makeText(getApplicationContext(), R.string.it_s_already_running_try_again_later,                                     Toast.LENGTH_LONG ).show();                 }             }         };         connect.setOnClickListener(l );     } ...

La sortie LogCat :

10-21 23:08:10.568: D/OERP(3393): We are connect to OpenERP, session id: 548d76c73ecd47bca7e3dd37dd1d3505 10-21 23:08:19.528: D/OERP(3393): Pierre Verkest lives in Orléans 10-21 23:08:19.528: D/OERP(3393): Demo Portal User lives in false 10-21 23:08:19.528: D/OERP(3393): Thomas Passot lives in Wavre 10-21 23:08:19.528: D/OERP(3393): Axelor lives in Champs sur Marne 10-21 23:08:19.528: D/OERP(3393): Chamber Works lives in Detroit 10-21 23:08:19.528: D/OERP(3393): Millennium Industries lives in London 10-21 23:08:19.528: D/OERP(3393): Zhi Ch'ang lives in Shanghai 10-21 23:08:19.528: D/OERP(3393): Nebula Business lives in Rosario 10-21 23:08:19.528: D/OERP(3393): Paul Williams lives in Fremont 10-21 23:08:19.528: D/OERP(3393): Vauxoo lives in Caracas

Connexion https :

Pour une connexion https, avec un certificat auto-signé. Une méthode est de créer un fichier Bouncy Castle KeyStore (.BKS) contenant la liste des certificats de confiance des serveurs openERP auxquels vous devez vous connecter. Un outil comme portecle peut être utilisé pour générer ce fichier.

Dans les fichiers sources, un exemple de BKS est présent (/res/raw/verkest.bks), il contient le certificat auto-signé pour le site test.openerp.verkest.fr, le mot de passe du porteclé est verkest.

Le compte openerp utilisé pour cette connexion est demo / demo.

La classe AnyboxHttpsClient permet d'étendre les fonctionnalités de la classe DefaultHttpClient afin de se connecter à des serveur http ou https. Il ajoute le certificat auto-signé à la liste des certificats de confiance.

/src/fr.anybox.openerpconnection/AnyboxHttpsClient.java :

package fr.anybox.openerpconnection;  import java.io.InputStream; import java.security.KeyStore;  import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.SingleClientConnManager;   import android.content.Context;   public class AnyboxHttpsClient extends DefaultHttpClient {     final Context context;          public AnyboxHttpsClient(Context context) {         this.context = context;     }           @Override     protected ClientConnectionManager createClientConnectionManager() {         SchemeRegistry registry = new SchemeRegistry();         registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));         // Register for port 443 our SSLSocketFactory with our keystore         // to the ConnectionManager         registry.register(new Scheme("https", newSslSocketFactory(), 443));         return new SingleClientConnManager(getParams(), registry);     }       private SSLSocketFactory newSslSocketFactory() {         try {             // Get an instance of the Bouncy Castle KeyStore format             KeyStore trusted = KeyStore.getInstance("BKS");             // Get the raw resource, which contains the keystore with             // your trusted certificates (root and any intermediate certs)             InputStream in = context.getResources().openRawResource(R.raw.verkest);             try {                 // Initialize the keystore with the provided trusted certificates                 // Also provide the password of the keystore                 trusted.load(in, "verkest".toCharArray());             } finally {                 in.close();             }             // Pass the keystore to the SSLSocketFactory. The factory is responsible             // for the verification of the server certificate.             SSLSocketFactory sf = new SSLSocketFactory(trusted);             // Hostname verification from certificate             // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506             // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d5e445             sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);             return sf;         } catch (Exception e) {             throw new AssertionError(e);         }     }  }

Dans la méthode DoInBackground de la classe OpenERPConnection, remplacer la ligne de création du client http :

//The http client that allow to keep the session open over requests HttpClient httpClient = new DefaultHttpClient(); Par //The https client that allow to keep the session open over requests HttpClient httpClient = new AnyboxHttpsClient();

Tester de nouveau, le résultat est le même qu'en http, si vous utilisez la même base.