La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

Clients mobiles MIDlet.

Présentations similaires


Présentation au sujet: "Clients mobiles MIDlet."— Transcription de la présentation:

1 Clients mobiles MIDlet

2 Plan Développer des MIDlet dans Eclipse Programmation de base
Installation de Java Wireless toolkit Installation de EclipseMe Programmation de base Programmation avancée Déploiement Fichier .jad Application descriptor Suite de MIDlets

3 Développer des MIDlet dans Eclipse
Installation de Java Wireless toolkit Installation de EclipseMe

4 Programmation de base HelloMIDlet HelloMIDlet2 FirstMIDlet
Lancement d’un MIDlet Choix du device Affichage Fin du midlet HelloMIDlet2 A ticker is an advertisement that runs across the top of the screen. FirstMIDlet Liste de commandes

5 Connexion The Connection interface The InputConnection interface
the most basic connection type. It can only be opened and closed. The InputConnection interface represents a device from which data can be read. Its openInputStream method returns an input stream for the connection. The OuputConnection interface represents a device to which data can be written. Its openOutputStream method returns an output stream for the connection. The StreamConnection interface combines the input and output connections. The ContentConnection a subinterface of StreamConnection. It provides access to some of the basic meta data information provided by HTTP connections. The StreamConnectionNotified waits for a connection to be established. It returns a StreamConnection on which a communication link has ben established. The DatagramConnection represents a datagram endpoint.

6 Établir une connexion Connector.open("http://java.sun.com/developer");
Connector.open("datagram://address:port#"); Connector.open("file:/myFile.txt"); Connector.open ("socket:// Connector.open("comm://9600:18N"); //port série État d’une connexion HTTP Setup: No connection yet Méthode disponibles qui font pas changer l’état setRequestMethod setRequestProperty Méthodes forçant le passage à l’état suivant openInputStream, openOutputStream openDataInputStream, openDataOutputStream getLength, getType, getDate, getExpiration Connected: Connection has been made, the request has been sent, and some response is expected Méthodes disponibles getURL, getProtocol, getHost, getPort Closed: Connection is closed

7 Connexion – MIDlets exemples
FirstExampleHTTP read the contents of a file referenced by a URL using StreamConnection. no HTTP-specific behavior needed. the Connector.open opens a connection to the URL and returns a StreamConnection object. Then an InputStream is opened through which to read the contents of the file, character by character, until the end of the file (-1). In the event an exception is thrown, both the stream and connection are closed. SecondExampleHTTP fetches a page, uses an HttpConnection. The Connector.open method opens a URL and returns an HttpConnection object. When you open the input stream, the connection is opened and the HTTP headers read. The c.getLength gets the content length, and if you want to get the content type, use the c.getType method. ThirdExampleHTTP an HttpConnection object returned by the call to the Connector.open method. The call to setRequestMethod is set to GET and some request headers are also set. a script is invoked that accepts a Student ID# from a form. The script searches a file and returns the final grade for the student corresponding to the ID#. .

8 Midlets – Interface usagers
MIDP GUI Programming: Programming the Phone Interface

9 Pourquoi ne pas utiliser AWT et Swing dans MIDP
AWT est conçu et optimisé pour les desktop de PC AWT présuppose un certain modèle d’interaction E.g. souris Plusieurs appareils comme les téléphones ne disposent pas de souris, mais uniquement de clavier. AWT un ensemble riche de composant principalement fondés pour le desktop Fenetres, layout, etc. Les objects sont créés dynamiquement au fur et à mesure des interactions Les capacités limitées de la mémoire et du CPU des PDAs sont mal adaptés à cette approche

10 Structure des APIs pour les interfaces usagers dans MIDP
API de haut-niveau Portabilité des applications est importante - utiliser une abstraction de haut-niveau Peu de contrôle sur le look and feel On ne peut pas décider de l’apparence visuelle des composantes : forme, couleur, fonte, etc. Interaction avec les composants est encapsulée dans l’implémentation Pas d’accès au mécanismes d’entrée comme les touches d’entrée. L’application n’est pas au courant des ces interactions. Encapsulation du scrolling, de la navigation et autres interactions primitives l’implémentation sous-jacente fait les adaptations nécessaires pour le matériel de l’appareil et pour le style natif de l’interface usager Assurer une expérience consistante entre les fonctions natives de l’appareil et celles des midlets. Les classes qui héritent de la classe Screen. Exemple d’application : information de traffic, météo… API de bas-niveau Peu d’abstraction. Pour les application qui ont besoin d’un placement précis, du contrôle des éléments graphiques et d’un accès aux événements d’entrée de bas-niveau L’ application a le contrôle complet sur ce qui est dessiné sur l’écran. Implémentée par les classes Canvas et Graphics. Pas de garantie de portabilité car l’API fournit des mécanismes pour accéder des détails qui sont dépendants de l’appareil. Exemple : jeux…

11 Le modèle de programmation du GUI pour MIDP
Abstraction principale: Screen Un seul écran visible à la fois, l’usager ne peut naviguer qu’à travers les éléments de cet écran. L’écran gère tous les événements au fur et à mesure de la navigation de l’usager Seul les événements de haut niveau sont transmis à l’application Les écrans ne « scrollent » que verticalement Il n y a pas de scroll horizontal Une application est composée d’un ensemble d’écrans à travers lesquels l’usager navigue au fur et à mesure qu’il complète la tâche. La classe Display est le gestionnaire d’affichage qui est instancié pour chaque midlet actif et qui fournit les méthodes pour récupérer les informations sur les capacités d’affichage de l’appareil. Une instance de Screen est rendue visible en invoquant la méthode Display.setCurrent(). A cause des petits écrans, absence de fenêtres ou de systèmes de fenêtres. Les appareils mobiles : des approches différentes des écrans et des claviers différents Si l’application doit se préoccuper du layout, du scrolling et du changements de focus, la portabilité est compromise.

12 Sortes de screens List, Alert, Text Box and Form List ou TextBox
Screens qui encapsulatent une composante d’interface usager complexe La structure de ces screens est prédéfinie L’application ne peut ajouter d’autres composantes à ce type de screens. List 3 variations Une liste implicite (menu); Une liste à choix unique; Une liste à choix multiples. Text boxes Ne supportent pas généralement un formattage complexe du texte ou des styles de fontes Permettent la capacité d’édition de base. Alert Utilise l’écran en entier. Forms Un écran générique L’application peut ajouter du texte, de l’image, et un ensemble simple de composantes UI. Autres Canvas pour utiliser l’API de bas niveau

13 Commandes abstraites Pour assurer la portabilité des applications entre les appareils isoler le développer de l’ensemble réel de boutons qui peuvent exister sur un appareil. clavier prévu pour usage à une seule main, clavier QWERTY prévu pour usage à plusieurs mains. Existence de boutons programmables Boutons dédiés pour Back, Help, Clear, L’API abstraite Commande La couche utilisée pour associer les boutons d’un appareil et un ensemble de commandes que le programmeur veut définir pour l’appareil Applications portables, mais pas de contrôle sur la manière dont une commande sera définis (placée) sur un appareil Chaque action est définie comme une commande et associée à un type, une priorité et une étiquette. Si trop de commandes sont définies, création d’un menu If an application asks for more abstract commands than there are available buttons, the MID Profile reference implementation, for example, uses another UI mechanism, the menu, to make the commands accessible to the user. The overflow of abstract commands is placed in a menu and the label Menu1 is mapped on to one of the programmable buttons. Other device implementations may use a menu mechanism or other method to handle the overflow of abstract commands. If an application asks for more abstract commands than there are available buttons, the MID Profile reference implementation, for example, uses another UI mechanism, the menu, to make the commands accessible to the user. The overflow of abstract commands is placed in a menu and the label Menu1 is mapped on to one of the programmable buttons. Other device implementations may use a menu mechanism or other method to handle the overflow of abstract commands. If an application asks for more abstract commands than there are available buttons, the MID Profile reference implementation, for example, uses another UI mechanism, the menu, to make the commands accessible to the user. The overflow of abstract commands is placed in a menu and the label Menu1 is mapped on to one of the programmable buttons. Other device implementations may use a menu mechanism or other method to handle the overflow of abstract commands.

14 Midlets – event handling
MIDP Event Handling 2 sortes d’événements l'événement Screen avec son listener correspondant CommandListener, la source d'un événement Command peut être tout objet Displayable Cette interface définit une méthode, commandAction, qui est appelée si une commande est déclenchée. l'événement ItemStateChanged avec le listener ItemStateListener. la source d'un événement ItemStateChanged ne peut être qu'un objet Form. Toute modification interactive de l'état d'un élément de formulaire déclenche un événement itemStateChanged (exemple : modification d'un texte, sélection d'un élément d'une liste, …). enregistrer l'objet ItemStateListener auprès d'un formulaire Form avec la méthode setItemStateListener (ItemStateListener myListener).

15 Callbacks Callbacks are invocation of programmer-defined methods that are executed by the application in response to actions taken by a user at run time There are four kinds of user interface callbacks in MIDP: Abstract commands that are part of the high-level API. Low-level events that represent single key presses and releases. Calls to the paint() method of a Canvas class. Calls to a Runnable object's run method requested by a call to the callSerially method of the Display class. All user interface callbacks are serialized. This means they can never occur in parallel. User interface callbacks are called as soon as the previous callback returns. In addition, the MIDP user interface API is thread-safe and includes a mechanism for event synchronization.

16

17 Exemples Event 1 Handling High-Level Events Event 2 Changing the Date
a list of items (Item1 to Item4) is created. The prepare() method is called whenever an item is selected. The testItem# methods, where # is a number between 1 and 4, call the prepare method and set the name of the menu. Event 2 Changing the Date a DateField object is created and added to a form. When you click on the date, you can change it by navigating through the calendar. When the date is changed, a message appears. Event 3 Handling Low-Level Events the Canvas class is subclassed to use an anonymous inner class, an implementation is provided for the keyPressed and keyReleased() methods, an empty implementation is provided for the paint method. When a key is pressed or released, the application prints the value of that key.

18 Livrer les événements de bas niveau
Création d’une sous-classe de Canvas protected void keyPressed(int keyCode) protected void keyReleased(int keyCode) protected void keyRepeated(int keyCode) protected void pointerPressed(int x, int y) protected void pointerDragged(int x, int y) protected void pointerReleased(int x, int y) protected void showNotify() protected void hideNotify() protected abstract void paint(Graphics g) commandAction() method of the CommandListener interface

19 KEY_NUM0 KEY_NUM1 KEY_NUM2 KEY_NUM3 KEY_NUM4 KEY_NUM5 KEY_NUM6 KEY_NUM7 KEY_NUM8 KEY_NUM9 KEY_STAR KEY_POUND DOWN LEFT RIGHT FIRE GAME_A GAME_B GAME_C GAME_D

20 Midlets – persistent storage
MIDP Database Programming Using RMS: a Persistent Storage for MIDlets

21 Persistence Un endroit non-volatile pour emmagasiner l’état des objets
Les objets peuvent continuer à exister après la fermeture de l’application qui les a créés. J2SE JDBC, sérialization Pas possible pour des appareils avec peu de mémoire MIDP Record Management System (RMS), un espace mémoire pour les MIDlets Une base de données Dans la plupart des appareils MIDP d’aujourd’hui, RMS est le seul moyen pour sauvegarder localement des données, Peu d’appareil possède un système conventionnel de fichiers. La spécification de MIDP exige de réserver au moins 8K pour de la mémoire non-volatile comme espace de stockage persistent. Toute suite de midlets qui utilise RMS devrait spécifier la quantité minimale d’octets dont elle a besoin à l’aide de l’attribut MIDlet-Data-Size, à la fois dans le fichier maniffest du fichier .jar et dans le fichier de description de lapplication (.jad) Si cet attribut est manquant, l’appareil suppose que le midlet n’a pas besoin d’espace de stockage.

22 Record stores (RS) RS Fichiers binaires dépendants de la plateforme car ils sont créés dans des répertoires dépendants de la plateforme une collection d’enregistrements avec un clé primaire unique (integer) L’ ID d’un enregistrement du premier enregistrement = 1 un compteur incrémenté de 1 à chaque création d’un enregistrement. Toutes les opérations individuelles sur un RS sont atomiques, synchrones et sérialisées Pas de corruption possible lors d’accès multiples dans un RS Si le midlet utilise plusieurs threads pour accéder un RS, le midlet est responsable de la synchronisation des accès sinon des enregistrements peuvent être réécrits ou effacés de manière inopportune.

23 Record stores (RS) RMS APIs: Noms javax.microedition.rms
Ajouter et supprimer des enregistrements les MIDlets de la même application peuvent partager des enregistrements Noms case sensitive, max. 32 caractères Une application peut créer plusieurs records stores avec des noms différents Un midlet ne peut pas créer deux RS avec le même nom dans la même application. Un midlet peut créer deux RS avec le même nom dans des applications différentes. Le RS est créé dans un répertoire NOJAM. Par exemple, si le répertoire de base est C:\J2MEWTK et le nom du RS est mystocks, Le RS est créé sous C:\J2MEWTK\NOJAM et s’appelle mystocks.db.

24 Un enregistrement… un record est un item de données individuel.
RMS ne met aucune restriction sur ce qui va dans un record: N’importe quelle séquence d’octets : nombre, chaîne de caractères, image… L’application est responsable de décoder la séquence d’octets. RMS fournit l’espace de stockage et un identificateur unique ID d’un enregistrement Ce n’est PAS un index Les destructions d’enregistrement n’entraine pas une renumérotation des enregistrements, ni n’affecte le prochain ID assigné.

25 Transformation en octets et vice-versa
Une instance de ByteArrayInputStream transforme un vecteur d’octets en flots d’entrée Une instance de ByteArrayOutputStream capture les données dans un tampon pour transformation ultérieure en vecteur d’octets Une instance de DataInputStream transforme un flot d’entrée en types primitifs et en chaînes de caractères Une instance de DataOutputStream écrit des chaines de caractères et des types primitifs dans un flot de sortie

26

27

28 Opérations de base Ouverture d’un RS Création d’un enregistrement
RecordStore db = RecordStore.openRecordStore("myDBfile", true); Si existe, rendre le RS existant 2e paramètre, si n’existe pas, créer le RS Création d’un enregistrement Pour manipuler les flots d’octets, utiliser les classes DataInputStream, DataOutputStream, ByteArrayInputStream, ByteArrayOutputStream On ne peut pas modifier une partie d’un enregistrement Il faut construire l’enregistrement au complet en mémoire sous la forme de vercteur d’octets, puis l’ajouter ou le modifier en un seul appel. ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeUTF(record); byte[] b = baos.toByteArray(); db.addRecord(b, 0, b.length);

29 Lecture d’un enregistrement
int recordID = .... // some record ID byte[] data = rs.getRecord( recordID ); ou int recordID = ...; // some record ID byte[] data = ...; // an array int offset = ...; // the starting offset int numCopied = rs.getRecord( recordID, data, offset ); byte[] data = new byte[ rs.getRecordSize( recordID ) ]; rs.getRecord( recordID, data, 0 );

30 Destruction d’un enregistrement
db.deleteRecord(id) il n’existe pas de méthode pour obtenir l’ID d’un enregistrement donné Une fois qu’un enregistrement est détruit toute tentative de l’utiliser déclenche une exception, InvalidRecordIDException.

31 Destruction d’un RS Un RS ne peut être détruit que s’il n’est pas ouvert et uniquement par un MIDLet de la suite qui possède le RS try { RecordStore.deleteRecordStore( "myrs" ); } catch( RecordStoreNotFoundException e ){ // no such record store } catch( RecordStoreException e ){ // somebody has it open }

32 Suivre l’évolution d’un RS
On peut savoir la dernière fois qu’un RS a été modifé time-stamp et version Pour un suivi plus serré, l’application peut s’enregistrer comme listener et être avertie quand un RS est modifié addRecordListener removeRecordListener

33 Comparaison d’enregistrements
Comparaison de deux Strings emmagasinées public someClas implements Comparator { public int compare(byte[] record1, byte[] record2) { ByteArrayInputStream bais1 = new ByteArrayInputStream(record1); DataInputStream dis1 = new DataInputStream(bais1); ByteArrayInputStream bais2 = new ByteArrayInputStream(record2); DataInputStream dis2 = new DataInputStream(bais2); String name1 = dis1.readUTF(); String name2 = dis.readUTF(); int num = name1.compareTo(name2); if (num > 0) { return RecordComparator.FOLLOWS; } else if (num < 0) { return recordcomparator.precedes; } else { return recordcomparator.equivalent; } } }

34 javax.microedition.rms

35 Exemple Base de données pour les cotations en bourse
Créer un RS Ajouter de nouveaux enregistrements de cotations L’usager spécifie le symbole de la cotation, e.g. SUNW, IBM, IT, MS, GM, Ford Le MIDlet obtient la cotation du serveur de cotation de Yahoo ( Construit l’enregistrement L’ajoute au RS Voir les enregistrements dans la base de données. Le MIDlet parcours les enregistrements du RS et les imprime formatés sur l’écran 3 classes: Stock.java, StockDB.java, QuotesMIDlet.java. Format des cotations Yahoo

36

37 Echanger des données entre midlets
Dans MIDP 1.0, chaque RS est privé pour une midlet Dans MIDP 2.0, les RS peuvent être partagés avec d’autres suites de midlets Éliminer les duplications Éviter les problèmes de synchronisation Simplifier la gestion Utiliser plus efficacement la mémoire

38 Partage de RS RS partagée
Identifié par un tuple (Vendor name, MIDlet suite name, Record store name) where: Vendor name : valeur de l’attribut MIDlet-Vendor spécifié dans le fichier JAD, le fichier manifest, ou les deux. MIDlet suite name : valeur de l’attribut MIDlet-Name spécifié dans le fichier JAD. Record store name : chaîne de caractère (max. 32 car.; case-sensitive) spécifié à la création du RS. Les paramètres authmode et writable sont ignorés si le RS existe déjà Propriétaire Responsable de la création, de l’identification du RS, du mode d’autorisation (partageable ou pas) et du mode d’accès (modifiable ou pas) static RecordStore openRecordStore( String recordStoreName, boolean create, // si true, le créer s’il n’existe pas int authmode, // RecordStore.AUTHMODE_PRIVATE // RecordStore.AUTHMODE_ANY boolean writable ); Consommateur accède au RS par son nom. static RecordStore openRecordStore( String recordStoreName, String vendorName, String suiteName ); // le nom de la suite qui possède le RS

39 Il est préférable de créer un RS partagé en mode AUTHMODE_PRIVATE, puis de le rendre accessible après son initialisation en changeant son mode.

40 Questions de sécurité Si le RS partagée est modifiable, n’importe quel midlet peut le modifier ou détruire ses données Stratégies pour ne pas rendre les données partagées aussi facilement accessibles Ne pas partager le RS. Perte des avantages du partage (optimisation de la mmoire, synchronisation, etc.). Encrypter les données et partager le RS en lecture seulement La clé doit être connue par tous les midlets autorisés ou encore entrées pas l’usager à l’exécution Le temps requis pour décrypter les données affecte la performance et consomme du CPU ce qui va réduire la durée de vie de la batterie Si la clé est compilée dans des midlets, elle peut être découverte. Rendre obscur le nom du RS. Si le nom est compilé dans des midlets, il peut être trouvé. Exiger l’authentification. Les données peuvent être lues, mais il faut s’authentifier pour le faire. Emmagasiner la clé sur un serveur et exiger l’authentification. L’ application s’authentifie sur un serveur et reçoit la clé en retour. La clé n’est jamais emmagasinée chez le client

41 Exemple Deux suite de midlets qui partagent une collection d’URLS
Par exemple pour identifier différent points d’accès dans un réseau corporatif Les classes sont URL URLdb ShareRMS UseRMS

42 Exercice Faire une midlet qui permet de gérer son bottin téléphonique

43 Ressources sur J2ME Java ME Technical Articles

44 MIDP et Midlet combiner CLDC avec Mobile Information Device Profile (MIDP) pour fournir un environnement Java pour les applications s’exécutant sur des téléphones cellulaires et des appareils avec des capacités similaires. MIDlet Application pour CLDC + MIDP par un logiciel application-management software (AMS) installé dans l’appareil La gestion externe de l’application est raisonnable car dans le contexte des appareils visés, l’application doit pouvoir être interrompue par des événements externes. Exemple, il faut pouvoir interrompre l’application pour permettre à l’usager de recevoir un appel téléphonique Placée dans un répertoire quelconque Idéalement l’usager devrait pouvoir faire une recherche pour ce type d’application et télécharger celle qui l’intéresse dans son appareil.

45 Le cycle de vie d’un midlet

46 Initialisation n’est faite qu’une seule fois
Centralise le code de nettoyage

47 Le cycle de vie d’un midlet
La classe principale d’un MIDlet doit être sous-classe de javax.microedition.midlet.MIDlet. Cette sous-classe définit 3 méthodes de notification liées au cycle de vie : startApp(), pauseApp(), destroyApp(). Il existe 3 états possibles reliés à son cycle de vie pour un MIDlet : paused: l’instance du MIDlet a été construite et est inactive. active: l’instance du MIDlet est active. destroyed: l’instance du MIDlet est terminée et est prête pour être réclamée par le ramasse-miettes. Il n’y a aucun équivalent de l’état loaded des applets. Le midlet doit être gérer lui-même son initialisation la première fois que sa méthode startApp() est invoquée. Les MIDlets sont créés par l’AMS, en général suite à une requête de l’usager. Par exemple, L’AMS affiche la liste des midlets disponibles et l’usager en choisit une. L’état initial d’un MIDlet est paused. A l’instar d’un applet, le MIDlet ne devrait pas faire d’initialisation dans son constructeur parce que son contexte d’exécution n’est pas encore mis en place. Après la construction du midlet, l’ AMS l’active et invoque sa méthode activates startApp(). Après avoir fait les initialisations nécessaires, la méthode startApp() crée et affiche l’interface usager. Après que la méthode startApp() ait rendu le contrôle, l’état deu MIDlet passe de paused à active. Si le MIDlet ne peut pas s’initialiser pour quelque raison que ce soit, il doit lancer une exception javax.microedition.midlet.MIDletStateChangeException – dans ce cas, son état passe immédiatement à destroyed. La désactiviation du MIDlet survient pour toute transition de l’état active à l’état paused. Le MIDlet n’est pas détruit, mais il devrait relâché le plus possible de ressources du système. Si la désactivation est déclenchée par l’AMS, la méthode pauseApp() du midlet est invoquée Si le MIDlet se désactive lui-même, par l’intermédiaire de son contexte d’opération, la méthode pauseApp() du midlet n’est PAS invoquée. La destruction du MIDlet survient lorsque le MIDlet passe dans l’état destroyed. Si la destruction est déclenchée par l’AMS, la méthode destroyApp() du MIDlet est invoquée. Une valeur booléene est transmise à cette méthode pour indiquer si la destruction est inconditionnelle (doit être détruit) ou optionnelle. Le MIDlet peut refusé une destruction optionnelle en lançant une exception MIDletStateChangeException. Si le MIDlet se détruit lui-même, la méthode destroyApp() du MIDlet n’est PAS invoquée.

48 Le contexte d’un MIDlet
Méthodes permettant à un MIDlet d’intergair avec son contexte getAppProperty() rend les valeurs des propriétés d’initialisation du MIDlet; Les propriétés d’initialisation sont des paires nom-valeur Elles sont recherchées dans le descripteur d’application (1er choix), un fichier texte qui contient de l’information sur un ensemble de midlet « packagées » ensemble dans un même fichier .jar (MIDlet suite) le fichier manifest (alternative) placé dans le .jar (MIDlet suite) qui contient le MIDlet Dans MIDP 2.0, si la suite est « trusted », la méthode getAppProperty() ne cherche que dans le manifest. resumeRequest() le MIDlet dans l’état paused invoque cette méthode pour demander à l’AMS d’être réactivé; c’est l’AMS qui décide si le MIDlet sera réactivé ou non notifyPaused() le MIDlet invoque cette méthode pour être désactivé; notifyDestroyed() : le MIDlet invoque cette méthode pour être détruit. Clarifier comment invoquer resumeRequest() et ses effets

49 References http://developers.sun.com/mobility/midp/articles/models/
A faire MAJ code quoteDB Directives d’importation de projet dans Eclipse Bug pour SharedRMS Threads Déploiement OTA Servlets Eclipse les parametres pour les projets J2ME

50 Installation EclipseMe
When installing the EclipseME feature, it is imperative that it be installed into the same directory structure as your base Eclipse installation. Locate the file config.ini. This file is located in the <eclipse install>/configuration directory. Edit the config.ini with your favorite text editor. Add the property definition osgi.framework.extensions=eclipseme.core.hooks to the configuration file. If the property already exists, append ",eclipseme.core.hooks" to the end of the current property definition.


Télécharger ppt "Clients mobiles MIDlet."

Présentations similaires


Annonces Google