<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>https://wikilipo.unige.ch/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Andreaskundig</id>
	<title>Wikilipo - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="https://wikilipo.unige.ch/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Andreaskundig"/>
	<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sp%C3%A9cial:Contributions/Andreaskundig"/>
	<updated>2026-05-07T07:23:30Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.39.4</generator>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Canvas&amp;diff=1054</id>
		<title>Canvas</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Canvas&amp;diff=1054"/>
		<updated>2011-12-09T09:41:41Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Voici un fichier html qui crée une une aiguille qui tourne. On peut le regarder ici : http://infolipo.net/cours/canvas/canvas.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;!-- interdire de zoomer --&amp;gt;&lt;br /&gt;
&amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0, user-scalable=no&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
 function start(){&lt;br /&gt;
   var exemple = document.getElementById(&#039;exemple&#039;);&lt;br /&gt;
   &lt;br /&gt;
   // angle de l&#039;aiguille&lt;br /&gt;
   var angle_en_degres = 0;&lt;br /&gt;
   function dessineImage(){&lt;br /&gt;
     // on change l&#039;angle&lt;br /&gt;
     angle_en_degres = angle_en_degres + 2; &lt;br /&gt;
     var angle_en_radians = angle_en_degres * Math.PI/180;&lt;br /&gt;
     dessineAiguille(angle_en_radians);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   // executer 60 fois par seconde dessineImage &lt;br /&gt;
   setInterval(dessineImage,1000/60);&lt;br /&gt;
&lt;br /&gt;
   /* une autre manière de faire&lt;br /&gt;
   function dessineAvecTimeout(){&lt;br /&gt;
     setTimeout(dessineAvecTimeout,1000/60);&lt;br /&gt;
     dessineImage();&lt;br /&gt;
   }&lt;br /&gt;
   dessineAvecTimeout();&lt;br /&gt;
   */&lt;br /&gt;
&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 function dessineAiguille(rotation){&lt;br /&gt;
  var exemple = document.getElementById(&#039;exemple&#039;);&lt;br /&gt;
  var contexte = exemple.getContext(&#039;2d&#039;);&lt;br /&gt;
&lt;br /&gt;
  // on enlève tout ce qui est dessiné&lt;br /&gt;
  contexte.clearRect(0,0,exemple.width,exemple.height);&lt;br /&gt;
&lt;br /&gt;
  // on enregistre l&#039;état du canvas&lt;br /&gt;
  // pour pouvoir y retourner après&lt;br /&gt;
  contexte.save();&lt;br /&gt;
&lt;br /&gt;
  // on se place au centre du canvas&lt;br /&gt;
  contexte.translate(exemple.width/2,exemple.height/2);&lt;br /&gt;
  // on dessine un rectangle au centre&lt;br /&gt;
  contexte.fillRect(-5, -5, 10, 10);&lt;br /&gt;
  // on tourne le canvas&lt;br /&gt;
  contexte.rotate(rotation);&lt;br /&gt;
  &lt;br /&gt;
  // on dessine l&#039;aiguille&lt;br /&gt;
  // on commence un chemin&lt;br /&gt;
  contexte.beginPath();&lt;br /&gt;
  // on se déplace au centre &lt;br /&gt;
  contexte.moveTo(0,0);&lt;br /&gt;
  // on tire un trait de 50 pixels vers la droite&lt;br /&gt;
  contexte.lineTo(0, 50);&lt;br /&gt;
  // on termine le chemin&lt;br /&gt;
  contexte.closePath();&lt;br /&gt;
  // on dessine le chemin&lt;br /&gt;
  contexte.stroke();&lt;br /&gt;
&lt;br /&gt;
  // on dessine un rectangle de 10 pixels de coté&lt;br /&gt;
  // à une hauteur de -5 pour qu&#039;il soit centré&lt;br /&gt;
  // et 50 pixels vers la droite pour qu&#039;il soit &lt;br /&gt;
  // au bout de l&#039;aiguille&lt;br /&gt;
  contexte.fillRect(-5, 50, 10, 10);&lt;br /&gt;
&lt;br /&gt;
  // on remet le canvas dans son état de départ&lt;br /&gt;
  // sans translation ni rotation&lt;br /&gt;
  contexte.restore();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;start();&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;canvas id=&amp;quot;exemple&amp;quot; width=&amp;quot;200&amp;quot; height=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
  Affichage d&#039;un texte pour les navigateurs qui ne supportent pas canvas.&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Voir aussi ce &amp;lt;a href=&amp;quot;https://developer.mozilla.org/fr/Tutoriel_canvas&amp;quot;&amp;gt;tutoriel&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Canvas&amp;diff=1053</id>
		<title>Canvas</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Canvas&amp;diff=1053"/>
		<updated>2011-12-09T09:39:35Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : Page créée avec « Ce fichier html crée une une aiguille qui tourne. On peut le regarder ici: http://infolipo.net/cours/canvas/canvas.html &amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt; &amp;lt;html&amp;gt;&amp;lt;head&amp;gt; &amp;lt;!-- prevent zoomi... »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ce fichier html crée une une aiguille qui tourne. On peut le regarder ici: http://infolipo.net/cours/canvas/canvas.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&lt;br /&gt;
&amp;lt;!-- prevent zooming --&amp;gt;&lt;br /&gt;
&amp;lt;meta name=&amp;quot;viewport&amp;quot; &lt;br /&gt;
  content=&amp;quot;width=device-width, initial-scale=1.0, user-scalable=no&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
 function start(){&lt;br /&gt;
   var exemple = document.getElementById(&#039;exemple&#039;);&lt;br /&gt;
   &lt;br /&gt;
   // angle de l&#039;aiguille&lt;br /&gt;
   var angle_en_degres = 0;&lt;br /&gt;
   function dessineImage(){&lt;br /&gt;
     // on change l&#039;angle&lt;br /&gt;
     angle_en_degres = angle_en_degres + 2; &lt;br /&gt;
     var angle_en_radians = angle_en_degres * Math.PI/180;&lt;br /&gt;
     dessineAiguille(angle_en_radians);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   // executer 60 fois par seconde dessineImage &lt;br /&gt;
   setInterval(dessineImage,1000/60);&lt;br /&gt;
&lt;br /&gt;
   /* une autre manière de faire&lt;br /&gt;
   function dessineAvecTimeout(){&lt;br /&gt;
     setTimeout(dessineAvecTimeout,1000/60);&lt;br /&gt;
     dessineImage();&lt;br /&gt;
   }&lt;br /&gt;
   dessineAvecTimeout();&lt;br /&gt;
   */&lt;br /&gt;
&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
 function dessineAiguille(rotation){&lt;br /&gt;
  var exemple = document.getElementById(&#039;exemple&#039;);&lt;br /&gt;
  var contexte = exemple.getContext(&#039;2d&#039;);&lt;br /&gt;
&lt;br /&gt;
  // on enlève tout ce qui est dessiné&lt;br /&gt;
  contexte.clearRect(0,0,exemple.width,exemple.height);&lt;br /&gt;
&lt;br /&gt;
  // on enregistre l&#039;état du canvas&lt;br /&gt;
  // pour pouvoir y retourner après&lt;br /&gt;
  contexte.save();&lt;br /&gt;
&lt;br /&gt;
  // on se place au centre du canvas&lt;br /&gt;
  contexte.translate(exemple.width/2,exemple.height/2);&lt;br /&gt;
  // on dessine un rectangle au centre&lt;br /&gt;
  contexte.fillRect(-5, -5, 10, 10);&lt;br /&gt;
  // on tourne le canvas&lt;br /&gt;
  contexte.rotate(rotation);&lt;br /&gt;
  &lt;br /&gt;
  // on dessine l&#039;aiguille&lt;br /&gt;
  // on commence un chemin&lt;br /&gt;
  contexte.beginPath();&lt;br /&gt;
  // on se déplace au centre &lt;br /&gt;
  contexte.moveTo(0,0);&lt;br /&gt;
  // on tire un trait de 50 pixels vers la droite&lt;br /&gt;
  contexte.lineTo(0, 50);&lt;br /&gt;
  // on termine le chemin&lt;br /&gt;
  contexte.closePath();&lt;br /&gt;
  // on dessine le chemin&lt;br /&gt;
  contexte.stroke();&lt;br /&gt;
&lt;br /&gt;
  // on dessine un rectangle de 10 pixels de coté&lt;br /&gt;
  // à une hauteur de -5 pour qu&#039;il soit centré&lt;br /&gt;
  // et 50 pixels vers la droite pour qu&#039;il soit &lt;br /&gt;
  // au bout de l&#039;aiguille&lt;br /&gt;
  contexte.fillRect(-5, 50, 10, 10);&lt;br /&gt;
&lt;br /&gt;
  // on remet le canvas dans son état de départ&lt;br /&gt;
  // sans translation ni rotation&lt;br /&gt;
  contexte.restore();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&lt;br /&gt;
&amp;lt;body onload=&amp;quot;start();&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;canvas id=&amp;quot;exemple&amp;quot; width=&amp;quot;200&amp;quot; height=&amp;quot;200&amp;quot;&amp;gt;&lt;br /&gt;
  Affichage d&#039;un texte pour les navigateurs qui ne supportent pas canvas.&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Voir aussi ce &amp;lt;a href=&amp;quot;https://developer.mozilla.org/fr/Tutoriel_canvas&amp;quot;&amp;gt;tutoriel&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1052</id>
		<title>Introduction au développement pour smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1052"/>
		<updated>2011-12-09T09:37:41Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Sencha touch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Pour développer une application mobile, on a d&#039;abord un très grand embarras du [[aperçu de technologies mobiles|choix]]. &lt;br /&gt;
&lt;br /&gt;
Même en privilégiant les solutions multiplateformes, faciles, pas chères et open source il reste encore des candidats. Faute d&#039;avoir le temps de tout essayer je me suis décidé un peu arbitrairement pour [http://phonegap.com/ phonegap] et [http://www.sencha.com/products/touch/ sencha touch].&lt;br /&gt;
&lt;br /&gt;
Le cours a lieu sur des machines windows. Il y a plein de logiciels à [[Installation des outils pour smartphone|installer]].&lt;br /&gt;
&lt;br /&gt;
== Phonegap ==&lt;br /&gt;
Phonegap permet de créer des applications pour mobile en utilisant html/css/javascript, et donne accès à des fonctionnalités du téléphone auxquelles une simple page web n&#039;a pas accès.&lt;br /&gt;
&lt;br /&gt;
Pour voir comment utiliser ces fonctionnalités, nous commençons par examiner le [[phonegap sample project|projet livré en exemple avec phonegap]]&lt;br /&gt;
&lt;br /&gt;
== Sencha touch ==&lt;br /&gt;
On peut construire soi-même son interface graphique à la main avec html/css, mais c&#039;est plus pratique d&#039;utiliser une librairie de composants graphiques comme sencha touch. Nous créons avec sencha touch une [[sencha touch contacts|application qui affiche les contacts du téléphone]].&lt;br /&gt;
&lt;br /&gt;
Ces exemples tirés de la documentation sont très instructifs, mais il est temps de se confronter à la réalité pour essayer d&#039;écrire une [[horaires de ma femme|application qui sert à quelque chose]].&lt;br /&gt;
&lt;br /&gt;
== Sinon ==&lt;br /&gt;
&lt;br /&gt;
Les navigateurs de smartphone sont tous plus ou moins compatibles avec html5. On peut profiter pour s&#039;amuser avec l&#039;élément [[canvas]]&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Solutions&amp;diff=1051</id>
		<title>Solutions</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Solutions&amp;diff=1051"/>
		<updated>2011-12-08T23:55:53Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : a déplacé Solutions vers Solutions cours smartphone&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECTION [[Solutions cours smartphone]]&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1050</id>
		<title>Solutions cours smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1050"/>
		<updated>2011-12-08T23:55:53Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : a déplacé Solutions vers Solutions cours smartphone&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== phonegap ==&lt;br /&gt;
&lt;br /&gt;
=== Exercice 1 ===&lt;br /&gt;
&lt;br /&gt;
Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
Ajouter un bouton dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;read_compass();&amp;quot;&amp;gt;Read Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce bouton appelle la fonction &#039;&#039;&#039;read_compass&#039;&#039;&#039; définie dans main.js. Le reste est copié de la documentation phonegap.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function read_compass(){&lt;br /&gt;
    navigator.compass.getCurrentHeading(onCompassSuccess, onCompassError);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassSuccess(heading){&lt;br /&gt;
    alert(&#039;Heading: &#039; + heading.magneticHeading);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exercice 2 ===&lt;br /&gt;
&lt;br /&gt;
Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
La solution est copiée sur la mesure de l&#039;accélération.&lt;br /&gt;
&lt;br /&gt;
Il faut ajouter un élément ou écrire la mesure de la boussole.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;Heading&amp;lt;/dt&amp;gt;&amp;lt;dd id=&amp;quot;heading&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut aussi ajouter un bouton pour déclencher et interrompre la mesure:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;toggleCompass();&amp;quot;&amp;gt;Toggle Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le code javascript est presque indentique à celui de toggleAccel:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var compassWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateCompass(heading) {&lt;br /&gt;
    document.getElementById(&#039;heading&#039;).innerHTML = heading.magneticHeading;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleCompass = function() {&lt;br /&gt;
    if (compassWatch !== null) {&lt;br /&gt;
        navigator.compass.clearWatch(compassWatch);&lt;br /&gt;
        updateCompass({magneticHeading:&amp;quot;&amp;quot;});&lt;br /&gt;
        compassWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        compassWatch = navigator.compass.watchHeading(&lt;br /&gt;
                updateCompass, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1049</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1049"/>
		<updated>2011-12-08T23:55:35Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exercices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///I:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== construire l&#039;application et l&#039;exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
Une autre solution:&lt;br /&gt;
# Autoriser l&#039;installation d&#039;applications ne provenant pas d&#039;Android Market: Réglages &amp;gt; Applications &amp;gt; Sources inconnues&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application. L&#039;application est construite sous la forme du fichier bin/Sample.apk&lt;br /&gt;
# copier le fichier bin/Sample.apk sur le téléphone, par exemple dans le répertoire download. &lt;br /&gt;
# ouvrir un explorateur et appuyer sur Sample.apk&lt;br /&gt;
# accepter l&#039;installation&lt;br /&gt;
&lt;br /&gt;
Une autre solution pour construire le fichier apk&lt;br /&gt;
# mettre tous les fichiers du répertoire assets/www dans une archive zip&lt;br /&gt;
# ouvrir un compte sur https://build.phonegap.com/&lt;br /&gt;
# uploader le fichier zip.&lt;br /&gt;
# attendre un peu&lt;br /&gt;
# télécharger le fichier apk.&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[solutions cours smartphone|solutions]]&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1048</id>
		<title>Solutions cours smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1048"/>
		<updated>2011-12-08T23:54:42Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Exercice 2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== phonegap ==&lt;br /&gt;
&lt;br /&gt;
=== Exercice 1 ===&lt;br /&gt;
&lt;br /&gt;
Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
Ajouter un bouton dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;read_compass();&amp;quot;&amp;gt;Read Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce bouton appelle la fonction &#039;&#039;&#039;read_compass&#039;&#039;&#039; définie dans main.js. Le reste est copié de la documentation phonegap.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function read_compass(){&lt;br /&gt;
    navigator.compass.getCurrentHeading(onCompassSuccess, onCompassError);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassSuccess(heading){&lt;br /&gt;
    alert(&#039;Heading: &#039; + heading.magneticHeading);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exercice 2 ===&lt;br /&gt;
&lt;br /&gt;
Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
La solution est copiée sur la mesure de l&#039;accélération.&lt;br /&gt;
&lt;br /&gt;
Il faut ajouter un élément ou écrire la mesure de la boussole.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;Heading&amp;lt;/dt&amp;gt;&amp;lt;dd id=&amp;quot;heading&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut aussi ajouter un bouton pour déclencher et interrompre la mesure:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;toggleCompass();&amp;quot;&amp;gt;Toggle Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le code javascript est presque indentique à celui de toggleAccel:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var compassWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateCompass(heading) {&lt;br /&gt;
    document.getElementById(&#039;heading&#039;).innerHTML = heading.magneticHeading;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleCompass = function() {&lt;br /&gt;
    if (compassWatch !== null) {&lt;br /&gt;
        navigator.compass.clearWatch(compassWatch);&lt;br /&gt;
        updateCompass({magneticHeading:&amp;quot;&amp;quot;});&lt;br /&gt;
        compassWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        compassWatch = navigator.compass.watchHeading(&lt;br /&gt;
                updateCompass, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1047</id>
		<title>Solutions cours smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1047"/>
		<updated>2011-12-08T23:52:32Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Exercice 2 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== phonegap ==&lt;br /&gt;
&lt;br /&gt;
=== Exercice 1 ===&lt;br /&gt;
&lt;br /&gt;
Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
Ajouter un bouton dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;read_compass();&amp;quot;&amp;gt;Read Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce bouton appelle la fonction &#039;&#039;&#039;read_compass&#039;&#039;&#039; définie dans main.js. Le reste est copié de la documentation phonegap.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function read_compass(){&lt;br /&gt;
    navigator.compass.getCurrentHeading(onCompassSuccess, onCompassError);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassSuccess(heading){&lt;br /&gt;
    alert(&#039;Heading: &#039; + heading.magneticHeading);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exercice 2 ===&lt;br /&gt;
&lt;br /&gt;
Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
La solution est copiée sur la mesure de l&#039;accélération.&lt;br /&gt;
&lt;br /&gt;
Il faut ajouter un élément ou écrire la mesure de la boussole.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;Heading&amp;lt;/dt&amp;gt;&amp;lt;dd id=&amp;quot;heading&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut aussi ajouter un bouton pour déclencher et interrompre la mesure:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;toggleCompass();&amp;quot;&amp;gt;toggle Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le code javascript est presque indentique à celui de toggleAccel:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var compassWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateCompass(heading) {&lt;br /&gt;
    document.getElementById(&#039;heading&#039;).innerHTML = heading.magneticHeading;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleCompass = function() {&lt;br /&gt;
    if (compassWatch !== null) {&lt;br /&gt;
        navigator.compass.clearWatch(compassWatch);&lt;br /&gt;
        updateCompass({heading:&amp;quot;&amp;quot;});&lt;br /&gt;
        compassWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        compassWatch = navigator.compass.watchHeading(&lt;br /&gt;
                updateCompass, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1046</id>
		<title>Solutions cours smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1046"/>
		<updated>2011-12-08T23:51:15Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* phonegap */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== phonegap ==&lt;br /&gt;
&lt;br /&gt;
=== Exercice 1 ===&lt;br /&gt;
&lt;br /&gt;
Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
Ajouter un bouton dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;read_compass();&amp;quot;&amp;gt;Read Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce bouton appelle la fonction &#039;&#039;&#039;read_compass&#039;&#039;&#039; définie dans main.js. Le reste est copié de la documentation phonegap.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function read_compass(){&lt;br /&gt;
    navigator.compass.getCurrentHeading(onCompassSuccess, onCompassError);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassSuccess(heading){&lt;br /&gt;
    alert(&#039;Heading: &#039; + heading.magneticHeading);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exercice 2 ===&lt;br /&gt;
&lt;br /&gt;
Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
La solution est copiée sur la mesure de l&#039;accélération.&lt;br /&gt;
&lt;br /&gt;
Il faut ajouter un élément ou écrire la mesure de la boussole.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;dl&amp;gt;&amp;lt;dt&amp;gt;Heading&amp;lt;/dt&amp;gt;&amp;lt;dd id=&amp;quot;heading&amp;quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/dd&amp;gt;&amp;lt;/dl&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il faut aussi ajouter un bouton pour déclencher et interrompre la mesure:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;toggleCompass();&amp;quot;&amp;gt;toggle Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le code javascript est presque indentique à celui de toggleAccel:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var compassWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateCompass(heading) {&lt;br /&gt;
    document.getElementById(&#039;heading&#039;).innerHTML = heading.magneticHeading;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleCompass = function() {&lt;br /&gt;
    if (compassWatch !== null) {&lt;br /&gt;
        navigator.compass.clearWatch(compassWatch);&lt;br /&gt;
        updateAcceleration({heading:&amp;quot;&amp;quot;});&lt;br /&gt;
        compassWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        compassWatch = navigator.compass.watchHeading(&lt;br /&gt;
                updateCompass, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1045</id>
		<title>Solutions cours smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Solutions_cours_smartphone&amp;diff=1045"/>
		<updated>2011-12-08T23:33:38Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : Page créée avec « == phonegap == Exercice 1: Cette application n&amp;#039;utilise pas la fonction de la boussole (&amp;#039;&amp;#039;&amp;#039;Compass&amp;#039;&amp;#039;&amp;#039;). Ajoutez un bouton qui indique l&amp;#039;angle que fait le téléphone avec le n... »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== phonegap ==&lt;br /&gt;
Exercice 1:&lt;br /&gt;
Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
&lt;br /&gt;
* Solution: &lt;br /&gt;
Ajouter un bouton dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;read_compass();&amp;quot;&amp;gt;Read Compass&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ce bouton appelle la fonction &#039;&#039;&#039;read_compass&#039;&#039;&#039; définie dans main.js. Le reste est copié de la documentation phonegap.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function read_compass(){&lt;br /&gt;
    navigator.compass.getCurrentHeading(onCompassSuccess, onCompassError);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// onSuccess: Get the current heading&lt;br /&gt;
//&lt;br /&gt;
function onCompassSuccess(heading){&lt;br /&gt;
    alert(&#039;Heading: &#039; + heading.magneticHeading);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// onError: Failed to get the heading&lt;br /&gt;
//&lt;br /&gt;
function onCompassError(compassError){&lt;br /&gt;
    alert(&#039;Compass Error: &#039; + compassError.code);&lt;br /&gt;
}&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1044</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1044"/>
		<updated>2011-12-08T23:27:30Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exercices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///I:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== construire l&#039;application et l&#039;exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
Une autre solution:&lt;br /&gt;
# Autoriser l&#039;installation d&#039;applications ne provenant pas d&#039;Android Market: Réglages &amp;gt; Applications &amp;gt; Sources inconnues&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application. L&#039;application est construite sous la forme du fichier bin/Sample.apk&lt;br /&gt;
# copier le fichier bin/Sample.apk sur le téléphone, par exemple dans le répertoire download. &lt;br /&gt;
# ouvrir un explorateur et appuyer sur Sample.apk&lt;br /&gt;
# accepter l&#039;installation&lt;br /&gt;
&lt;br /&gt;
Une autre solution pour construire le fichier apk&lt;br /&gt;
# mettre tous les fichiers du répertoire assets/www dans une archive zip&lt;br /&gt;
# ouvrir un compte sur https://build.phonegap.com/&lt;br /&gt;
# uploader le fichier zip.&lt;br /&gt;
# attendre un peu&lt;br /&gt;
# télécharger le fichier apk.&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[[solutions|solutions cours smartphone]]&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1043</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1043"/>
		<updated>2011-12-08T23:17:51Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exercices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///I:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== construire l&#039;application et l&#039;exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
Une autre solution:&lt;br /&gt;
# Autoriser l&#039;installation d&#039;applications ne provenant pas d&#039;Android Market: Réglages &amp;gt; Applications &amp;gt; Sources inconnues&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application. L&#039;application est construite sous la forme du fichier bin/Sample.apk&lt;br /&gt;
# copier le fichier bin/Sample.apk sur le téléphone, par exemple dans le répertoire download. &lt;br /&gt;
# ouvrir un explorateur et appuyer sur Sample.apk&lt;br /&gt;
# accepter l&#039;installation&lt;br /&gt;
&lt;br /&gt;
Une autre solution pour construire le fichier apk&lt;br /&gt;
# mettre tous les fichiers du répertoire assets/www dans une archive zip&lt;br /&gt;
# ouvrir un compte sur https://build.phonegap.com/&lt;br /&gt;
# uploader le fichier zip.&lt;br /&gt;
# attendre un peu&lt;br /&gt;
# télécharger le fichier apk.&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
[solutions|solutions cours smartphone]&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1042</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1042"/>
		<updated>2011-12-08T23:03:13Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exécuter sur un téléphone android */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///I:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== construire l&#039;application et l&#039;exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
Une autre solution:&lt;br /&gt;
# Autoriser l&#039;installation d&#039;applications ne provenant pas d&#039;Android Market: Réglages &amp;gt; Applications &amp;gt; Sources inconnues&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application. L&#039;application est construite sous la forme du fichier bin/Sample.apk&lt;br /&gt;
# copier le fichier bin/Sample.apk sur le téléphone, par exemple dans le répertoire download. &lt;br /&gt;
# ouvrir un explorateur et appuyer sur Sample.apk&lt;br /&gt;
# accepter l&#039;installation&lt;br /&gt;
&lt;br /&gt;
Une autre solution pour construire le fichier apk&lt;br /&gt;
# mettre tous les fichiers du répertoire assets/www dans une archive zip&lt;br /&gt;
# ouvrir un compte sur https://build.phonegap.com/&lt;br /&gt;
# uploader le fichier zip.&lt;br /&gt;
# attendre un peu&lt;br /&gt;
# télécharger le fichier apk.&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1041</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1041"/>
		<updated>2011-12-08T22:59:32Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exécuter sur un téléphone android */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///I:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
Une autre solution:&lt;br /&gt;
# Autoriser l&#039;installation d&#039;applications ne provenant pas d&#039;Android Market: Réglages &amp;gt; Applications &amp;gt; Sources inconnues&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application. L&#039;application est construite sous la forme du fichier bin/Sample.apk&lt;br /&gt;
# copier le fichier bin/Sample.apk sur le téléphone, par exemple dans le répertoire download. &lt;br /&gt;
# ouvrir un explorateur et appuyer sur Sample.apk&lt;br /&gt;
# accepter l&#039;installation&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1040</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1040"/>
		<updated>2011-12-08T22:54:56Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exécuter sur un téléphone android */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///I:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
Une autre solution:&lt;br /&gt;
# Autoriser l&#039;installation d&#039;applications ne provenant pas d&#039;Android Market: Réglages &amp;gt; Applications &amp;gt; Sources inconnues&lt;br /&gt;
# copier le fichier bin/Sample.apk sur le téléphone. &lt;br /&gt;
# ouvrir un explorateur et appuyer sur le fichier Sample.apk&lt;br /&gt;
# accepter l&#039;installation&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1039</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1039"/>
		<updated>2011-12-08T19:58:26Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exercices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///I:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1038</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1038"/>
		<updated>2011-12-07T13:37:59Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exécuter dans l&amp;#039;émulateur ripple */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///I:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;. Phonegap ne fournit pas de fonction qui déclenche une mesure régulière, mais le même effet peut être obtenu en utilisant la méthode javascript &#039;&#039;&#039;setInterval&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1037</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1037"/>
		<updated>2011-12-07T13:24:44Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== copier l&#039;exemple phonegap ==&lt;br /&gt;
&lt;br /&gt;
Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous I:/work. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///H:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
# dans le projet Sample, effacer project.properties. Ce fichier est automatiquement généré par eclipse.&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;. Phonegap ne fournit pas de fonction qui déclenche une mesure régulière, mais le même effet peut être obtenu en utilisant la méthode javascript &#039;&#039;&#039;setInterval&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1036</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1036"/>
		<updated>2011-12-02T13:54:08Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot; enclose=&amp;quot;none&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
            //pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
                     // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
       // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
       // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
       var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	               {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
                        iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
                        iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
		      }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	        dock: &#039;bottom&#039;,&lt;br /&gt;
	        defaults: {iconMask: true},&lt;br /&gt;
                layout: {pack: &#039;center&#039;},&lt;br /&gt;
                items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                         text: &#039;reset&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                        }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
		        }, &lt;br /&gt;
                        {iconCls: &#039;compose&#039;,&lt;br /&gt;
			 text: &#039;post&#039;,&lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
		}]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
    // On envoie les données sur le serveur par ajax.&lt;br /&gt;
    // On peut faire des requetes ajax directement en javascript&lt;br /&gt;
    // mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
    // Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
       // l&#039;adresse du serveur&lt;br /&gt;
       // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
       // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
       params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
                data:horaires},&lt;br /&gt;
				&lt;br /&gt;
       // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
       // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# Ajouter un bouton pour appeler le syndicat&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
===  Améliorations hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A part ça ce serait utile de profiter de html5 pour permettre à l&#039;application de tourner offline: http://www.sencha.com/learn/taking-sencha-touch-apps-offline/&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1035</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1035"/>
		<updated>2011-12-02T11:41:21Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* application.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot; enclose=&amp;quot;none&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
            //pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
                     // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
       // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
       // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
       var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	               {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
                        iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
                        iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
		      }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	        dock: &#039;bottom&#039;,&lt;br /&gt;
	        defaults: {iconMask: true},&lt;br /&gt;
                layout: {pack: &#039;center&#039;},&lt;br /&gt;
                items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                         text: &#039;reset&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                        }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
		        }, &lt;br /&gt;
                        {iconCls: &#039;compose&#039;,&lt;br /&gt;
			 text: &#039;post&#039;,&lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
		}]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
    // On envoie les données sur le serveur par ajax.&lt;br /&gt;
    // On peut faire des requetes ajax directement en javascript&lt;br /&gt;
    // mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
    // Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
       // l&#039;adresse du serveur&lt;br /&gt;
       // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
       // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
       params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
                data:horaires},&lt;br /&gt;
				&lt;br /&gt;
       // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
       // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# Ajouter un bouton pour appeler le syndicat&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
===  Améliorations hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1034</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1034"/>
		<updated>2011-12-02T11:36:58Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* ossature du projet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&amp;lt;html&amp;gt;&lt;br /&gt;
&amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(version 0)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js (version 1),&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# Trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# Trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup. Déployer ce code pour voir l&#039;alerte.(application.js version 2)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
//On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application.js version 3):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1033</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1033"/>
		<updated>2011-12-02T11:36:22Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* ossature du projet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
 &amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
 &amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(version 0)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js (version 1),&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# Trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# Trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup. Déployer ce code pour voir l&#039;alerte.(application.js version 2)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
//On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application.js version 3):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1032</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1032"/>
		<updated>2011-12-02T11:15:01Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* application.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
            //pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
                     // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
       // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
       // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
       var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	               {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
                        iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
                        iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
		      }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	        dock: &#039;bottom&#039;,&lt;br /&gt;
	        defaults: {iconMask: true},&lt;br /&gt;
                layout: {pack: &#039;center&#039;},&lt;br /&gt;
                items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                         text: &#039;reset&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                        }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
		        }, &lt;br /&gt;
                        {iconCls: &#039;compose&#039;,&lt;br /&gt;
			 text: &#039;post&#039;,&lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
		}]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
    // On envoie les données sur le serveur par ajax.&lt;br /&gt;
    // On peut faire des requetes ajax directement en javascript&lt;br /&gt;
    // mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
    // Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
       // l&#039;adresse du serveur&lt;br /&gt;
       // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
       // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
       params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
                data:horaires},&lt;br /&gt;
				&lt;br /&gt;
       // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
       // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# Ajouter un bouton pour appeler le syndicat&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
===  Améliorations hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1031</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1031"/>
		<updated>2011-12-02T11:13:40Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* application.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
            //pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
                     // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
       // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
       // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
       var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	        dock: &#039;bottom&#039;,&lt;br /&gt;
	        defaults: {iconMask: true},&lt;br /&gt;
                layout: {pack: &#039;center&#039;},&lt;br /&gt;
                items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                         text: &#039;reset&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                        }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
		        }, &lt;br /&gt;
                        {iconCls: &#039;compose&#039;,&lt;br /&gt;
			 text: &#039;post&#039;,&lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
		}]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
    // On envoie les données sur le serveur par ajax.&lt;br /&gt;
    // On peut faire des requetes ajax directement en javascript&lt;br /&gt;
    // mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
    // Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
       // l&#039;adresse du serveur&lt;br /&gt;
       // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
       // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
       params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
                data:horaires},&lt;br /&gt;
				&lt;br /&gt;
       // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
       // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# Ajouter un bouton pour appeler le syndicat&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
===  Améliorations hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1030</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1030"/>
		<updated>2011-12-02T11:11:32Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* application.js */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
			//pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
					 // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
       // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
       // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
       var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	        dock: &#039;bottom&#039;,&lt;br /&gt;
	        defaults: {iconMask: true},&lt;br /&gt;
                layout: {pack: &#039;center&#039;},&lt;br /&gt;
                items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                         text: &#039;reset&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                        }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
		        }, &lt;br /&gt;
                        {iconCls: &#039;compose&#039;,&lt;br /&gt;
			 text: &#039;post&#039;,&lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
		}]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
    // On envoie les données sur le serveur par ajax.&lt;br /&gt;
    // On peut faire des requetes ajax directement en javascript&lt;br /&gt;
    // mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
    // Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
       // l&#039;adresse du serveur&lt;br /&gt;
       // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
       // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
       params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
                data:horaires},&lt;br /&gt;
				&lt;br /&gt;
       // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
       // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# Ajouter un bouton pour appeler le syndicat&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
===  Améliorations hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1029</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1029"/>
		<updated>2011-12-02T11:09:32Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
			//pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
					 // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
       // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
       // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
       var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	        dock: &#039;bottom&#039;,&lt;br /&gt;
	        defaults: {iconMask: true},&lt;br /&gt;
                layout: {pack: &#039;center&#039;},&lt;br /&gt;
                items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                         text: &#039;reset&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                        }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
		        }, &lt;br /&gt;
                        {iconCls: &#039;compose&#039;,&lt;br /&gt;
			 text: &#039;post&#039;,&lt;br /&gt;
                         listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
		}]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
	// Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
	// et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
	// On envoie les données sur le serveur par ajax.&lt;br /&gt;
	// On peut faire des requetes ajax directement en javascript&lt;br /&gt;
	// mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
	// Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
	   // l&#039;adresse du serveur&lt;br /&gt;
	   // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
	   // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
	   params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
	            data:horaires},&lt;br /&gt;
				&lt;br /&gt;
	   // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
	   // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# Ajouter un bouton pour appeler le syndicat&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
===  Améliorations hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1028</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1028"/>
		<updated>2011-12-02T11:06:21Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
			//pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
					 // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
	   // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
	   // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
	   var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	   	dock: &#039;bottom&#039;,&lt;br /&gt;
		defaults: {iconMask: true},&lt;br /&gt;
        layout: {pack: &#039;center&#039;},&lt;br /&gt;
        items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                 text: &#039;reset&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                 }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
				 }, &lt;br /&gt;
                {iconCls: &#039;compose&#039;,&lt;br /&gt;
				 text: &#039;post&#039;,&lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
				 }]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
	// Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
	// et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
	// On envoie les données sur le serveur par ajax.&lt;br /&gt;
	// On peut faire des requetes ajax directement en javascript&lt;br /&gt;
	// mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
	// Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
	   // l&#039;adresse du serveur&lt;br /&gt;
	   // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
	   // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
	   params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
	            data:horaires},&lt;br /&gt;
				&lt;br /&gt;
	   // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
	   // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# Ajouter un bouton pour appeler le syndicat&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
===  Améliorations hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1027</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1027"/>
		<updated>2011-12-02T10:59:42Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Améliorer la fonctionnalité */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
			//pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
					 // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
	   // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
	   // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
	   var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	   	dock: &#039;bottom&#039;,&lt;br /&gt;
		defaults: {iconMask: true},&lt;br /&gt;
        layout: {pack: &#039;center&#039;},&lt;br /&gt;
        items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                 text: &#039;reset&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                 }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
				 }, &lt;br /&gt;
                {iconCls: &#039;compose&#039;,&lt;br /&gt;
				 text: &#039;post&#039;,&lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
				 }]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
	// Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
	// et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
	// On envoie les données sur le serveur par ajax.&lt;br /&gt;
	// On peut faire des requetes ajax directement en javascript&lt;br /&gt;
	// mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
	// Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
	   // l&#039;adresse du serveur&lt;br /&gt;
	   // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
	   // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
	   params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
	            data:horaires},&lt;br /&gt;
				&lt;br /&gt;
	   // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
	   // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
# Ajouter un bouton pour appeler le syndicat&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
===  Exercices hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1026</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1026"/>
		<updated>2011-12-02T10:57:49Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Améliorer l&amp;#039;interface graphique */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
			//pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
					 // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
	   // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
	   // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
	   var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	   	dock: &#039;bottom&#039;,&lt;br /&gt;
		defaults: {iconMask: true},&lt;br /&gt;
        layout: {pack: &#039;center&#039;},&lt;br /&gt;
        items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                 text: &#039;reset&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                 }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
				 }, &lt;br /&gt;
                {iconCls: &#039;compose&#039;,&lt;br /&gt;
				 text: &#039;post&#039;,&lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
				 }]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
	// Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
	// et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
	// On envoie les données sur le serveur par ajax.&lt;br /&gt;
	// On peut faire des requetes ajax directement en javascript&lt;br /&gt;
	// mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
	// Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
	   // l&#039;adresse du serveur&lt;br /&gt;
	   // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
	   // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
	   params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
	            data:horaires},&lt;br /&gt;
				&lt;br /&gt;
	   // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
	   // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Autre solution: remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Exercices hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1025</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1025"/>
		<updated>2011-12-02T10:56:23Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exercices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
			//pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
					 // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
	   // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
	   // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
	   var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	   	dock: &#039;bottom&#039;,&lt;br /&gt;
		defaults: {iconMask: true},&lt;br /&gt;
        layout: {pack: &#039;center&#039;},&lt;br /&gt;
        items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                 text: &#039;reset&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                 }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
				 }, &lt;br /&gt;
                {iconCls: &#039;compose&#039;,&lt;br /&gt;
				 text: &#039;post&#039;,&lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
				 }]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
	// Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
	// et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
	// On envoie les données sur le serveur par ajax.&lt;br /&gt;
	// On peut faire des requetes ajax directement en javascript&lt;br /&gt;
	// mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
	// Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
	   // l&#039;adresse du serveur&lt;br /&gt;
	   // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
	   // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
	   params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
	            data:horaires},&lt;br /&gt;
				&lt;br /&gt;
	   // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
	   // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Comprendre l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
# Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Comprendre le fonctionnement de l&#039;application ===&lt;br /&gt;
&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Améliorer l&#039;interface graphique ===&lt;br /&gt;
&lt;br /&gt;
# Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
# Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
## Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action.&lt;br /&gt;
## Remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Améliorer la fonctionnalité ===&lt;br /&gt;
&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===  Exercices hardcore ===&lt;br /&gt;
&lt;br /&gt;
# Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
# Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1024</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1024"/>
		<updated>2011-12-02T10:51:12Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ceci est une application qui résoud un problème concret. &lt;br /&gt;
&lt;br /&gt;
Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application.&lt;br /&gt;
&lt;br /&gt;
L&#039;application doit permettre à ma femme de saisir rapidement son horaire du mois suivant quand il est affiché. Comme j&#039;ai moi aussi besoin de connaitre les horaires de ma femme, et que ma mère aimerait les savoir aussi. L&#039;application doit pouvoir publier l&#039;horaire sur internet.&lt;br /&gt;
&lt;br /&gt;
Cette application fonctionne, mais elle est loin d&#039;être finie. &lt;br /&gt;
&lt;br /&gt;
== application.js ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
			//pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
					 // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
	   // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
	   // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
	   var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	   	dock: &#039;bottom&#039;,&lt;br /&gt;
		defaults: {iconMask: true},&lt;br /&gt;
        layout: {pack: &#039;center&#039;},&lt;br /&gt;
        items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                 text: &#039;reset&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                 }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
				 }, &lt;br /&gt;
                {iconCls: &#039;compose&#039;,&lt;br /&gt;
				 text: &#039;post&#039;,&lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
				 }]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
	// Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
	// et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
	// On envoie les données sur le serveur par ajax.&lt;br /&gt;
	// On peut faire des requetes ajax directement en javascript&lt;br /&gt;
	// mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
	// Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
	   // l&#039;adresse du serveur&lt;br /&gt;
	   // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
	   // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
	   params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
	            data:horaires},&lt;br /&gt;
				&lt;br /&gt;
	   // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
	   // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit en premier de décortiquer l&#039;application pour comprendre ce quelle fait, et en suite de l&#039;améliorer&lt;br /&gt;
&lt;br /&gt;
# Comprendre l&#039;interface graphique&lt;br /&gt;
## Déplacer la barre des taches du bas de l&#039;écran vers le haut&lt;br /&gt;
## Rajouter une rangée de boutons&lt;br /&gt;
&lt;br /&gt;
# Comprendre le fonctionnement de l&#039;application&lt;br /&gt;
## Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
## Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
## Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
# Améliorer l&#039;interface graphique&lt;br /&gt;
## Créer une plus belle template pour afficher les rangées.&lt;br /&gt;
##* Les boutons &#039;&#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039; sont trop accessibles, ils pourraient être appuyés par erreur&lt;br /&gt;
##*# Ouvrir un dialogue de confirmation qui dit &amp;quot;vraiment?&amp;quot;, avec un bouton &#039;&#039;&#039;Interrompre&#039;&#039;&#039; et un bouton &#039;&#039;&#039;OK&#039;&#039;&#039; qui déclenche l&#039;action&lt;br /&gt;
##*# Remplacer les trois boutons par un bouton &#039;&#039;&#039;actions&#039;&#039;&#039; qui ouvre un panneau avec &#039;&#039;save&#039;&#039;&#039;, &#039;&#039;&#039;reset&#039;&#039;&#039; et &#039;&#039;&#039;post&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Améliorer la fonctionnalité&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# Les horaires sont identifiés par un nom, mais ils correspondent à des heures de début et de fin. Comment intégrer les heures dans l&#039;application?&lt;br /&gt;
## Tenir compte des horaires coupés qui sont fait d&#039;une période de travail le matin, et d&#039;une autre le soir.&lt;br /&gt;
&lt;br /&gt;
# Exercices hardcore&lt;br /&gt;
## Trouver comment faire pour que les boutons reflètent l&#039;état du jour sélectionné. Je n&#039;ai pas trouvé comment faire en sorte qu&#039;un bouton reste appuyé. Je n&#039;ai pas voulu utilisé les radiobuttons qui me semblaient trop volumineux. Est-ce qu&#039;il faut créer des boutons soi-même avec html/css?&lt;br /&gt;
## Publier les horaires sous forme d&#039;évènements dans un agenda en ligne google. Ce serait très utile parce que les agendas d&#039;android et d&#039;iphone peuvent être synchronisés avec un agenda google.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1023</id>
		<title>Horaires de ma femme</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Horaires_de_ma_femme&amp;diff=1023"/>
		<updated>2011-12-02T10:20:26Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : Page créée avec « Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&amp;#039;un jour à l&amp;#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&amp;#039;ici ell... »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ma femme est aide-soignante dans un EMS. Ses heures de travail changent d&#039;un jour à l&#039;autre. Elle apprend au courant du mois ses horaires pour le mois suivant. Jusqu&#039;ici elle arrivait à se procurer une photocopie des horaires. Malheureusement, un règlement absurde interdit de les photocopier et la direction a récemment décidé d&#039;appliquer ce règlement. Nous ferions mieux d&#039;appeler le syndicat, mais comme je suis informaticien, j&#039;écris plutôt une application:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
	  &lt;br /&gt;
       Ext.regModel(&#039;Day&#039;, {fields: [&#039;date&#039;,&#039;horaire&#039;]} );&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
            itemTpl : &#039;{date}: {horaire}&#039;,&lt;br /&gt;
			singleSelect: true,&lt;br /&gt;
			//pour raccourcir le code, je crée le store ici.&lt;br /&gt;
            store: new Ext.data.JsonStore({&lt;br /&gt;
                     model  : &#039;Day&#039;,&lt;br /&gt;
					 // voir la définition de loadData plus bas&lt;br /&gt;
                     data: loadData()})&lt;br /&gt;
       });      &lt;br /&gt;
&lt;br /&gt;
       // Les configurations qui vont être utilisées pour les boutons.&lt;br /&gt;
	   // Les attributs qui sont pareils pour tous les boutons (xtype, height, width, listeners)&lt;br /&gt;
	   // vont être ajoutés par défaut par la fonction makeButtonPannel&lt;br /&gt;
       var buttons1 = [{text: &#039;1&#039;},{text: &#039;2&#039;},{text: &#039;3&#039;},{text: &#039;3s&#039;},{text: &#039;4&#039;},{ text: &#039;5&#039;}];&lt;br /&gt;
	   var buttons2 = [{text: &#039;ce&#039;},{text: &#039;f&#039;},{text: &#039;r&#039;},{text: &#039;v&#039;},{text: &#039;fo&#039;},&lt;br /&gt;
	                   {&lt;br /&gt;
                        // Les listeners sont des fonctions qui sont appelées lorqu&#039;un événement a lieu.&lt;br /&gt;
                        // Ici on a un listener qui réagit à l&#039;événement &#039;tap&#039;, qui est envoyé&lt;br /&gt;
                        // quand on touche le bouton. Pour un exemple d&#039;utilisation d&#039;autres événements&lt;br /&gt;
                        // voir sencha-touch-1.1.0/examples/kitchensink/src/demos/touch.js&lt;br /&gt;
                        // Comme ce bouton a déjà un attribut listeners, il ne va pas recevoir&lt;br /&gt;
                        // les listeners par défaut donnés par makeButtonPanel&lt;br /&gt;
                        listeners: {&lt;br /&gt;
                           &#039;tap&#039;: function () {selectPreviousNode(list);}&lt;br /&gt;
                           },&lt;br /&gt;
                        // Ce dernier bouton contient une icone à la place du texte.&lt;br /&gt;
                        // Ca ne fonctionne pas très bien avec l&#039;agrandissement des boutons.&lt;br /&gt;
                        // Pour faire cela correctement, voir&lt;br /&gt;
                        // http://www.sencha.com/forum/showthread.php?150028-Scaling-button-icons&lt;br /&gt;
					   	iconCls: &#039;arrow_up&#039;, &lt;br /&gt;
					    iconMask: true // necessary if the icon is encoded as base64&lt;br /&gt;
					    }];&lt;br /&gt;
       var size = screen.width/Math.max(buttons1.length,buttons2.length);&lt;br /&gt;
       var buttonPanel1 =  makeButtonPannel(buttons1,size,list);&lt;br /&gt;
       var buttonPanel2 =  makeButtonPannel(buttons2,size,list);&lt;br /&gt;
&lt;br /&gt;
       var titleToolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;horaires&#039; });&lt;br /&gt;
       var bottomToolbar = new Ext.Toolbar({&lt;br /&gt;
	   	dock: &#039;bottom&#039;,&lt;br /&gt;
		defaults: {iconMask: true},&lt;br /&gt;
        layout: {pack: &#039;center&#039;},&lt;br /&gt;
        items: [{iconCls: &#039;delete&#039;,&lt;br /&gt;
                 text: &#039;reset&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {resetData(list);}}&lt;br /&gt;
                 }, &lt;br /&gt;
		        {iconCls: &#039;action&#039;, &lt;br /&gt;
		         text: &#039;save&#039;, &lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {saveData(list);}}&lt;br /&gt;
				 }, &lt;br /&gt;
                {iconCls: &#039;compose&#039;,&lt;br /&gt;
				 text: &#039;post&#039;,&lt;br /&gt;
                 listeners: {&#039;tap&#039;: function () {postData(list);}}&lt;br /&gt;
				 }]          &lt;br /&gt;
&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
			// Le layout &#039;fit&#039; remplit tout avec le seul item qu&#039;il contient&lt;br /&gt;
			// Sans cette ligne la liste dépasserait sous les boutons&lt;br /&gt;
			// voir la doc http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.FitLayout &lt;br /&gt;
            layout: &#039;fit&#039;, &lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [titleToolbar,bottomToolbar,buttonPanel2,buttonPanel1]&lt;br /&gt;
       });&lt;br /&gt;
	   &lt;br /&gt;
	   &lt;br /&gt;
       list.select(0);&lt;br /&gt;
&lt;br /&gt;
 }});&lt;br /&gt;
&lt;br /&gt;
// Crée un panel de boutons à partir de:&lt;br /&gt;
// buttons: un array de configurations de boutons&lt;br /&gt;
// size: le coté d&#039;un bouton (ils sont carrés)&lt;br /&gt;
// list: la liste sur laquelle agissent les boutons&lt;br /&gt;
function makeButtonPannel(buttons, size, list){&lt;br /&gt;
       return new Ext.Panel({&lt;br /&gt;
            // quand on utilise ce panel comme dockedItem d&#039;un autre panel &lt;br /&gt;
            // il apparait en bas&lt;br /&gt;
            dock: &#039;bottom&#039;,&lt;br /&gt;
            layout: {&lt;br /&gt;
                // layout qui pose ses items côte à côte horizontalement&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.layout.HBoxLayout&lt;br /&gt;
                type : &#039;hbox&#039;,&lt;br /&gt;
                pack : &#039;center&#039;&lt;br /&gt;
            },&lt;br /&gt;
            // hauteur du panel: size passé en argument de la fonction&lt;br /&gt;
            height: size,&lt;br /&gt;
            // attributs qui vont s&#039;ajouter aux attributs des items.&lt;br /&gt;
            defaults: {&lt;br /&gt;
                // Les items auront le type Button&lt;br /&gt;
                // pour la correspondance entre xtypes et classes&lt;br /&gt;
                // voir http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
                xtype: &#039;button&#039;,&lt;br /&gt;
                height: size,&lt;br /&gt;
                width: size,&lt;br /&gt;
                listeners: {&lt;br /&gt;
                   &#039;tap&#039;: function(button, event){ setHoraire(button.text,list);}&lt;br /&gt;
                }&lt;br /&gt;
            },&lt;br /&gt;
            // la liste de boutons passés en argument&lt;br /&gt;
            items: buttons&lt;br /&gt;
        });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Positionne l&#039;horaire à la date sélectionnée dans la liste&lt;br /&gt;
// et sélectionne l&#039;élément suivant.&lt;br /&gt;
function setHoraire(horaire,list){&lt;br /&gt;
  // Il doit y en avoir un unique élément sélectionné&lt;br /&gt;
 var selectedRecord = list.getSelectedRecords()[0]; &lt;br /&gt;
 var index = list.indexOf(selectedRecord);&lt;br /&gt;
 &lt;br /&gt;
 // C&#039;est un modèle. Pour le modifier il faut utiliser la fonction set&lt;br /&gt;
 // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
 selectedRecord.set(&#039;horaire&#039;,horaire);&lt;br /&gt;
 // Il faut rafraichir le node pour que le changement soit visible&lt;br /&gt;
 list.refreshNode(index);&lt;br /&gt;
 &lt;br /&gt;
 // sélectionner la ligne suivante&lt;br /&gt;
 if(index &amp;lt; list.getStore().getCount() - 1){&lt;br /&gt;
         list.select(index + 1);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Recule la sélection d&#039;une position dans la list&lt;br /&gt;
function selectPreviousNode(list){&lt;br /&gt;
   var selectedNode = list.getSelectedNodes()[0]; //il doit y en avoir un&lt;br /&gt;
   var selectedIndex = list.indexOf(selectedNode); &lt;br /&gt;
   var newIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;&lt;br /&gt;
   list.select(newIndex);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
 // Enregistre les horaires sous la forme d&#039;un string,&lt;br /&gt;
 // par exemple &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
function saveData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
	var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5).&lt;br /&gt;
	// Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
	// et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    localStorage[&amp;quot;horaires&amp;quot;] = horaires;   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lit les données enregistrées et les retourne sous forme d&#039;un array&lt;br /&gt;
// de configuraions du modèle Day&lt;br /&gt;
// par exemple [{date: &amp;quot;Thu 1.12.2011&amp;quot;, horaire: &amp;quot;4&amp;quot;}, {...}, ... ]&lt;br /&gt;
function loadData(){&lt;br /&gt;
    // Ce programme n&#039;est pas encore capable de gérer plusieurs mois.&lt;br /&gt;
    // On décide arbitrairement qu&#039;on est en décembre 2011&lt;br /&gt;
    var year = 2011;&lt;br /&gt;
    // les mois sont numérotés de 0 à 11, 11 est décembre&lt;br /&gt;
    var month = 11; &lt;br /&gt;
    &lt;br /&gt;
    // les données enregistrées. &lt;br /&gt;
    // localStorage est un objet fourni par javascript (html 5)&lt;br /&gt;
    // Les données qu&#039;on y met sont enregistrées dans le browser&lt;br /&gt;
    // et sont disponibles à chaque fois qu&#039;on va sur la page.&lt;br /&gt;
    var horaires = localStorage[&amp;quot;horaires&amp;quot;];&lt;br /&gt;
    if(horaires){&lt;br /&gt;
        return DAY.horairesToRecordConfigs(year,month,horaires)&lt;br /&gt;
    }else{&lt;br /&gt;
        return DAY.makeEmptyRecordConfigs(year,month);&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Remet tous les horaires de la liste à &amp;quot;-&amp;quot;&lt;br /&gt;
function resetData(list){&lt;br /&gt;
    var records = list.getStore().data;&lt;br /&gt;
    for(var i=0; i&amp;lt; records.length; i++){&lt;br /&gt;
        // records est une MixedCollection, sur laquelle il faut appeler la méthode get.&lt;br /&gt;
        // record est un modèle. Pour le modifier il faut utiliser la méthode set&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model &lt;br /&gt;
        records.get(i).set(&amp;quot;horaire&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Envoie les horaires sur un serveur.&lt;br /&gt;
function postData(list){&lt;br /&gt;
    // Array de tous les modèles Day&lt;br /&gt;
    var records = list.getStore().data.getRange();&lt;br /&gt;
    // string  &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    var horaires = DAY.recordsToHoraires(records);&lt;br /&gt;
	&lt;br /&gt;
	// On envoie les données sur le serveur par ajax.&lt;br /&gt;
	// On peut faire des requetes ajax directement en javascript&lt;br /&gt;
	// mais c&#039;est plus confortable de passer par une librairie.&lt;br /&gt;
	// Ici nous utilisons celle qui est fournie par sencha.&lt;br /&gt;
    Ext.Ajax.request({&lt;br /&gt;
		&lt;br /&gt;
	   // l&#039;adresse du serveur&lt;br /&gt;
	   // J&#039;y ai mis un programme php qui enregistre les données pour un nom.&lt;br /&gt;
       url: &amp;quot;http://infolipo.net/cours/data/data.php&amp;quot;, &lt;br /&gt;
&lt;br /&gt;
	   // C&#039;est gentil de remplacer &amp;quot;andreas&amp;quot; par votre nom pour ne pas écraser mes données.&lt;br /&gt;
       // On peut voir ce qui est enregistré à la page&lt;br /&gt;
       // http://infolipo.net/cours/data/data.php?name=andreas&lt;br /&gt;
	   params: {name:&amp;quot;andreas&amp;quot;,&lt;br /&gt;
	            data:horaires},&lt;br /&gt;
				&lt;br /&gt;
	   // Cette fonction est appelée au retour de la requête, si tout a bien marché&lt;br /&gt;
       success: function(response, opts) {&lt;br /&gt;
         alert(&amp;quot;Réponse du serveur: &amp;quot; + response.responseText);&lt;br /&gt;
       },&lt;br /&gt;
	   &lt;br /&gt;
	   // Cette fonction est appelée en cas d&#039;échec.&lt;br /&gt;
       failure: function(response, opts) {&lt;br /&gt;
         alert(&#039;échec: &#039; + response.status);&lt;br /&gt;
       }&lt;br /&gt;
   });	&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Les fonctions pour manipuler le modèle Day ont assez en commun&lt;br /&gt;
// pour que je les regroupe dans un même objet, je trouve.&lt;br /&gt;
// C&#039;est une question de gout.&lt;br /&gt;
var DAY = {&lt;br /&gt;
	&lt;br /&gt;
 // Crée les configuration de modèles pour un mois &lt;br /&gt;
 // year: l&#039;année&lt;br /&gt;
 // month: le mois&lt;br /&gt;
 // horaires: un string représentant les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 horairesToRecordConfigs: function(year, month, horaires){&lt;br /&gt;
    // les horaires sont enregistrés sous la forme &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    // sépare les horaires pour avoir un array [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = horaires.split(&amp;quot;|&amp;quot;);&lt;br /&gt;
    var day = 0;&lt;br /&gt;
    return horairesEnArray.map(function(h){ &lt;br /&gt;
            var date = new Date(year,month,day);&lt;br /&gt;
            var config = DAY.createRecordConfig(h,date);&lt;br /&gt;
            day ++;&lt;br /&gt;
            return config;&lt;br /&gt;
          }&lt;br /&gt;
       ); &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée un string qui représente les horaires &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
 // à partir d&#039;un array de modèles Day&lt;br /&gt;
 recordsToHoraires: function(records){&lt;br /&gt;
    // On tire des records un array qui contient les horaires dans l&#039;ordre&lt;br /&gt;
    // par exemple [&amp;quot;3s&amp;quot;,&amp;quot;4&amp;quot;,&amp;quot;r&amp;quot;,...]&lt;br /&gt;
    var horairesEnArray = records.map(function(record){&lt;br /&gt;
        // C&#039;est un modèle. Il faut utiliser la méthode get&lt;br /&gt;
        // http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
        return record.get(&amp;quot;horaire&amp;quot;);&lt;br /&gt;
    });&lt;br /&gt;
    // retourner un string &amp;quot;3s|4|r|...&amp;quot;&lt;br /&gt;
    return horairesEnArray.join(&amp;quot;|&amp;quot;);	&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array de configurations pour le modèle Day,&lt;br /&gt;
 // toutes avec l&#039;horaire &amp;quot;-&amp;quot;, &lt;br /&gt;
 // pour tous les jours du mois indiqué &lt;br /&gt;
 makeEmptyRecordConfigs: function(year, month){&lt;br /&gt;
       // prendre l&#039;array de dates fourni par datesOfMonth()&lt;br /&gt;
       // et le transformer en array de configurations pour le modèle Day.&lt;br /&gt;
       var datesData = DAY.datesOfMonth(year,month).map(&lt;br /&gt;
          function(d){ return DAY.createRecordConfig(&amp;quot;-&amp;quot;,d);}&lt;br /&gt;
       ); &lt;br /&gt;
       return datesData;&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Crée la configuration pour un modèle Day&lt;br /&gt;
 createRecordConfig: function(l_horaire, la_date){&lt;br /&gt;
   // la méthode format est ajoutée à Date par sencha&lt;br /&gt;
   // voir http://docs.sencha.com/touch/1-1/#!/api/Date&lt;br /&gt;
   return {&lt;br /&gt;
      date: la_date.format(&amp;quot;D d.m.Y&amp;quot;),&lt;br /&gt;
      horaire: l_horaire&lt;br /&gt;
  };    &lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
 // Retourne un array contenant les objets Date du mois indiqué&lt;br /&gt;
 datesOfMonth: function(year, month){&lt;br /&gt;
	var date = new Date(year, month, 1);&lt;br /&gt;
	var month = date.getMonth()&lt;br /&gt;
	var dates = [];&lt;br /&gt;
	date.setDate(1);&lt;br /&gt;
	while (month === date.getMonth()) {&lt;br /&gt;
		dates.push(date);&lt;br /&gt;
		// la méthode add est ajoutée par sencha&lt;br /&gt;
		// à la classe Date fournie par javascript&lt;br /&gt;
		date = date.add(Date.DAY, 1);&lt;br /&gt;
	}&lt;br /&gt;
	return dates;&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pour finir nous examinons une application dans un état plus avancé, mais qui est loin d&#039;être finie. L&#039;exercice constitue à décortiquer l&#039;application, et à la compléter:&lt;br /&gt;
# Mettre tout en haut la barre des taches qui est tout en bas.&lt;br /&gt;
# Ajouter une rangée de boutons&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# L&#039;application n&#039;est pas très belle, comment pourrait on l&#039;améliorer?&lt;br /&gt;
## Créer une meilleure template pour afficher les rangées.&lt;br /&gt;
## Peut-on remplacer les boutons par du css/html fait à la main? (Je n&#039;en suis pas sur. J&#039;aimerais bien avoir une espèce de bouton qui reste appuyé en fonction de la rangée sélectionnée.)&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1022</id>
		<title>Introduction au développement pour smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1022"/>
		<updated>2011-12-02T10:15:20Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Sencha touch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Pour développer une application mobile, on a d&#039;abord un très grand embarras du [[aperçu de technologies mobiles|choix]]. &lt;br /&gt;
&lt;br /&gt;
Même en privilégiant les solutions multiplateformes, faciles, pas chères et open source il reste encore des candidats. Faute d&#039;avoir le temps de tout essayer je me suis décidé un peu arbitrairement pour [http://phonegap.com/ phonegap] et [http://www.sencha.com/products/touch/ sencha touch].&lt;br /&gt;
&lt;br /&gt;
Le cours a lieu sur des machines windows. Il y a plein de logiciels à [[Installation des outils pour smartphone|installer]].&lt;br /&gt;
&lt;br /&gt;
== Phonegap ==&lt;br /&gt;
Phonegap permet de créer des applications pour mobile en utilisant html/css/javascript, et donne accès à des fonctionnalités du téléphone auxquelles une simple page web n&#039;a pas accès.&lt;br /&gt;
&lt;br /&gt;
Pour voir comment utiliser ces fonctionnalités, nous commençons par examiner le [[phonegap sample project|projet livré en exemple avec phonegap]]&lt;br /&gt;
&lt;br /&gt;
== Sencha touch ==&lt;br /&gt;
On peut construire soi-même son interface graphique à la main avec html/css, mais c&#039;est plus pratique d&#039;utiliser une librairie de composants graphiques comme sencha touch. Nous créons avec sencha touch une [[sencha touch contacts|application qui affiche les contacts du téléphone]].&lt;br /&gt;
&lt;br /&gt;
Ces exemples tirés de la documentation sont très instructifs, mais il est temps de se confronter à la réalité pour essayer d&#039;écrire une [[horaires de ma femme|application qui sert à quelque chose]].&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1021</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1021"/>
		<updated>2011-12-02T10:09:03Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(version 0)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js (version 1),&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# Trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# Trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup. Déployer ce code pour voir l&#039;alerte.(application.js version 2)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
//On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application.js version 3):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1020</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1020"/>
		<updated>2011-12-02T10:07:35Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(version 0)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js (version 1),&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# Trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# Trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup.(application.js version 2)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
//On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Déployer et voir l&#039;alerte&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application.js version 3):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1019</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1019"/>
		<updated>2011-12-02T09:57:49Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(application0.js)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js,&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
(application1.js)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# Trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# Trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup.(application2.js)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Déployer et voir l&#039;alerte&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application3.js):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1018</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1018"/>
		<updated>2011-12-02T09:54:57Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(application0.js)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js,&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
(application1.js)&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;  itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/source&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt; getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# Trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# Trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;source lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/slurce&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;source lang=&amp;quot;hmtl5&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup.(application2.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Déployer et voir l&#039;alerte&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application3.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;pre&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1017</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1017"/>
		<updated>2011-12-02T09:47:23Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous H:/work. &lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///H:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html5&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;. Phonegap ne fournit pas de fonction qui déclenche une mesure régulière, mais le même effet peut être obtenu en utilisant la méthode javascript &#039;&#039;&#039;setInterval&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1016</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1016"/>
		<updated>2011-12-02T09:46:09Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous H:/work. &lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///H:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;. Phonegap ne fournit pas de fonction qui déclenche une mesure régulière, mais le même effet peut être obtenu en utilisant la méthode javascript &#039;&#039;&#039;setInterval&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1015</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1015"/>
		<updated>2011-12-02T09:25:50Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous H:/work. &lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///H:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;source lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;. Phonegap ne fournit pas de fonction qui déclenche une mesure régulière, mais le même effet peut être obtenu en utilisant la méthode javascript &#039;&#039;&#039;setInterval&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1014</id>
		<title>Phonegap sample project</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Phonegap_sample_project&amp;diff=1014"/>
		<updated>2011-12-02T09:24:32Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ce projet est une bonne démonstration des fonctionnalités auxquelles phonegap permet d&#039;accéder. Le projet se trouve sous H:/callback-phonegap-9f0856d/Android/Sample. Il faut le copier sous H:/work. &lt;br /&gt;
&lt;br /&gt;
== exécuter l&#039;application ==&lt;br /&gt;
=== exécuter dans l&#039;émulateur ripple ===&lt;br /&gt;
&lt;br /&gt;
Ouvrir chrome à cette adresse: file:///H:/work/Sample/assets/www/index.html. &lt;br /&gt;
Un click droit ouvre un menu avec Emulator, choisir enable.&lt;br /&gt;
&lt;br /&gt;
=== importer le projet dans eclipse ===&lt;br /&gt;
&lt;br /&gt;
# File&amp;gt;New&amp;gt;Other &lt;br /&gt;
# Android&amp;gt;Android project&lt;br /&gt;
# name: Sample, (x) Create project from existing source, Location: H:/work/Sample&lt;br /&gt;
# next version Android 4.0, next minimum SDK 10(10 Android 4.0), finish&lt;br /&gt;
# il y a des erreurs de compilation&lt;br /&gt;
# dans l&#039;arborescence Sample&amp;gt;libs&amp;gt;phonegap-1.1.0.jar, click droit Build Path&amp;gt;Add to Build Path &lt;br /&gt;
# L&#039;erreur de compilation disparait&lt;br /&gt;
&lt;br /&gt;
(A un moment j&#039;ai eu problème avec project.properties, il a fallu écrire à la main la valeur target=android-14. Mais ce problème ne s&#039;est pas reproduit)&lt;br /&gt;
&lt;br /&gt;
=== exécuter sur un téléphone android ===&lt;br /&gt;
&lt;br /&gt;
# installer le driver usb pour adb de l&#039;appareil&lt;br /&gt;
# le brancher le téléphone sur usb &lt;br /&gt;
# (si on a rooté son appareil, on peut travailler sans usb avec l&#039;app adbWireless)&lt;br /&gt;
# autoriser le débogage sur le téléphone: Réglages&amp;gt;Applications&amp;gt;Développement&amp;gt;Débogage USB&lt;br /&gt;
# dans eclipse, click droit sur Sample, Run As&amp;gt;1 Android Application&lt;br /&gt;
# l&#039;application démarre sur le téléphone&lt;br /&gt;
&lt;br /&gt;
== examiner l&#039;exemple ==&lt;br /&gt;
&lt;br /&gt;
Les fichiers de l&#039;application sont sous Sample/assets/www/&lt;br /&gt;
&#039;&#039;&#039;index.html&#039;&#039;&#039; &lt;br /&gt;
* inclut les autres fichiers: phonegap.js, main.js, master.css&lt;br /&gt;
* appelle des fonctions javascript dans body onload et a onclick&lt;br /&gt;
&#039;&#039;&#039;main.css&#039;&#039;&#039;&lt;br /&gt;
* définit les styles des éléments&lt;br /&gt;
&#039;&#039;&#039;main.js&#039;&#039;&#039; &lt;br /&gt;
* contient les fonctions javascript appelées dans &#039;&#039;&#039;index.html&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Le code javascript utilise des objets javascript créés par phonegap.js comme &#039;&#039;device&#039;&#039;, navigator.&#039;&#039;accelerometer&#039;&#039;. Ces objets ne sont disponible qu&#039;après l&#039;initialisation de phonegap, qui est signalée par l&#039;événement &#039;&#039;deviceready&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== init ===&lt;br /&gt;
La fonction &#039;&#039;&#039;init&#039;&#039;&#039;, appelée au chargement d&#039;index.html fait en sorte que la fonction &#039;&#039;deviceInfo&#039;&#039; n&#039;est appelée que quand phonegap est prêt. La fonction deviceInfo lit des propriétés de l&#039;objet device, comme device.platform etc...&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function init() {&lt;br /&gt;
    document.addEventListener(&amp;quot;deviceready&amp;quot;, deviceInfo, true);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== beep ===&lt;br /&gt;
Les autres fonctions, comme &#039;&#039;&#039;beep&#039;&#039;&#039;, sont appelées lorsqu&#039;on clique sur un bouton.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; class=&amp;quot;btn large&amp;quot; onclick=&amp;quot;beep();&amp;quot;&amp;gt;Beep&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;beep&#039;&#039;&#039; est une fonction très simple:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var beep = function() {&lt;br /&gt;
    navigator.notification.beep(2);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== show_pic ===&lt;br /&gt;
&#039;&#039;&#039;show_pic&#039;&#039;&#039; est plus compliquée, elle passe à getPicture les fonctions &#039;&#039;&#039;dump_pic&#039;&#039;&#039; et &#039;&#039;&#039;fail&#039;&#039;&#039;. &#039;&#039;&#039;fail&#039;&#039;&#039; est appelée s&#039;il y a eu un problème. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function show_pic() {&lt;br /&gt;
    navigator.camera.getPicture(dump_pic, fail, {&lt;br /&gt;
        quality : 50&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dump_pic ===&lt;br /&gt;
&#039;&#039;&#039;dump_pic&#039;&#039;&#039; s&#039;occupe de la photo en cas de succès. Elle reçoit en argument les données de la photo.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function dump_pic(data) {&lt;br /&gt;
    //...&lt;br /&gt;
    document.getElementById(&amp;quot;test_img&amp;quot;).src = &amp;quot;data:image/jpeg;base64,&amp;quot; + data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour savoir exactement comment utiliser ces fonctions, on peut aussi consulter l&#039;excellente documentation de phonegap&lt;br /&gt;
http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html#Camera&lt;br /&gt;
&lt;br /&gt;
Pour chaque fonctionnalité, la documentation indique aussi sur quels plateformes elle est disponible.&lt;br /&gt;
&lt;br /&gt;
=== toggleAccel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;toggleAccel&#039;&#039;&#039; est la plus compliquée des fonctions de l&#039;exemple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var accelerationWatch = null;&lt;br /&gt;
&lt;br /&gt;
function updateAcceleration(a) {&lt;br /&gt;
    document.getElementById(&#039;x&#039;).innerHTML = roundNumber(a.x);&lt;br /&gt;
    document.getElementById(&#039;y&#039;).innerHTML = roundNumber(a.y);&lt;br /&gt;
    document.getElementById(&#039;z&#039;).innerHTML = roundNumber(a.z);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var toggleAccel = function() {&lt;br /&gt;
    if (accelerationWatch !== null) {&lt;br /&gt;
        navigator.accelerometer.clearWatch(accelerationWatch);&lt;br /&gt;
        updateAcceleration({&lt;br /&gt;
            x : &amp;quot;&amp;quot;,&lt;br /&gt;
            y : &amp;quot;&amp;quot;,&lt;br /&gt;
            z : &amp;quot;&amp;quot;&lt;br /&gt;
        });&lt;br /&gt;
        accelerationWatch = null;&lt;br /&gt;
    } else {&lt;br /&gt;
        var options = {};&lt;br /&gt;
        options.frequency = 1000;&lt;br /&gt;
        accelerationWatch = navigator.accelerometer.watchAcceleration(&lt;br /&gt;
                updateAcceleration, &lt;br /&gt;
                function(ex) {&lt;br /&gt;
                    alert(&amp;quot;accel fail (&amp;quot; + ex.name + &amp;quot;: &amp;quot; + ex.message + &amp;quot;)&amp;quot;);&lt;br /&gt;
                }, options);&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ici c&#039;est plus simple de regarder d&#039;abord la documentation. http://docs.phonegap.com/en/1.2.0/phonegap_accelerometer_accelerometer.md.html#Accelerometer&lt;br /&gt;
Phonegap fournit trois méthodes pour l&#039;accéléromètre, et ce code n&#039;en utilise que deux: &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; et &#039;&#039;&#039;clearWatch&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; fait en sorte qu&#039; à intervalle régulier &#039;&#039;&#039;updateAcceleration&#039;&#039;&#039; est appelée avec les données de l&#039;accéléromètre&lt;br /&gt;
* L&#039;appel à &#039;&#039;&#039;watchAcceleration&#039;&#039;&#039; retourne une variable d&#039;identification appelée dans l&#039;exemple &#039;&#039;&#039;accelerationWatch&#039;&#039;&#039; . Pour interrompre les mesures d&#039;accélération, on peut appeler &#039;&#039;&#039;clearWatch(accelerationWatch)&#039;&#039;&#039;&lt;br /&gt;
* On voit que la fonction &#039;&#039;&#039;toggleAccel&#039;&#039;&#039; déclenche la mesure de l&#039;accélération la première fois qu&#039;elle est appelée. A l&#039;appel suivant elle interrompt la mesure, et à l&#039;appel d&#039;après elle le remet en marche etc...&lt;br /&gt;
&lt;br /&gt;
== exercices ==&lt;br /&gt;
# Cette application n&#039;utilise pas la fonction de la boussole (&#039;&#039;&#039;Compass&#039;&#039;&#039;). Ajoutez un bouton qui indique l&#039;angle que fait le téléphone avec le nord. Afficher la valeur de l&#039;angle avec la fonction &#039;&#039;&#039;notification.alert&#039;&#039;&#039; fournie par phonegap.&lt;br /&gt;
# Ce n&#039;est pas pratique d&#039;avoir le nord que quand on appuie un bouton. Modifiez la fonction pour qu&#039;elle affiche l&#039;angle à intervalle régulier, d&#039;une manière similaire à &#039;&#039;&#039;toggleAccel&#039;&#039;&#039;. Phonegap ne fournit pas de fonction qui déclenche une mesure régulière, mais le même effet peut être obtenu en utilisant la méthode javascript &#039;&#039;&#039;setInterval&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== autres fonctionnalités de phonegap ==&lt;br /&gt;
Comme on peut voir dans la documentation, phonegap donne l&#039;accès à d&#039;autres fonctionnalités du téléphone:&lt;br /&gt;
* &#039;&#039;&#039;Capture&#039;&#039;&#039; permet d&#039;enregistrer de l&#039;audio, des vidéos et des photos&lt;br /&gt;
* &#039;&#039;&#039;Connection&#039;&#039;&#039; permet de savoir si l&#039;appareil est connecté à internet&lt;br /&gt;
* &#039;&#039;&#039;Contacts&#039;&#039;&#039; donne accès au carnet d&#039;adresses&lt;br /&gt;
* &#039;&#039;&#039;Events&#039;&#039;&#039; permet de réagir à des évènements propres au téléphone. Nous avons déjà vu &lt;br /&gt;
** &#039;&#039;&#039;deviceready&#039;&#039;&#039; est lancé quand phonegap est initialisé. &lt;br /&gt;
** &#039;&#039;&#039;pause&#039;&#039;&#039;/&#039;&#039;&#039;resume&#039;&#039;&#039; sont déclenchés quand l&#039;application est mise en arrière/avant-plan. &lt;br /&gt;
** &#039;&#039;&#039;online&#039;&#039;&#039;/&#039;&#039;&#039;offline&#039;&#039;&#039; signalent la connexion/deconnexion à internet&lt;br /&gt;
** &#039;&#039;&#039;batterycritical&#039;&#039;&#039;, &#039;&#039;&#039;batterylow&#039;&#039;&#039;, &#039;&#039;&#039;batterystatus&#039;&#039;&#039; réagissent à des changement du  niveau de batterie.&lt;br /&gt;
** &#039;&#039;&#039;backbutton&#039;&#039;&#039;, &#039;&#039;&#039;menubutton&#039;&#039;&#039;, &#039;&#039;&#039;searchbutton&#039;&#039;&#039; etc... signalent qu&#039;un bouton a été pressé sur le téléphone, et ne sont disponibles que sur les téléphones qui ont ces boutons&lt;br /&gt;
* &#039;&#039;&#039;File&#039;&#039;&#039; permet d&#039;écrire et de lire des fichiers sur le téléphone.&lt;br /&gt;
* &#039;&#039;&#039;Geolocation&#039;&#039;&#039; donne la latitude et longitude du téléphone. (Cette information est aussi disponibles au pages web).&lt;br /&gt;
* &#039;&#039;&#039;Media&#039;&#039;&#039; permet d&#039;enregistrer et de lire des fichiers audio.&lt;br /&gt;
* &#039;&#039;&#039;Storage&#039;&#039;&#039; donne l&#039;accès à une base de donnée relationnelle avec laquelle on peut communiquer avec le langage SQL.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1013</id>
		<title>Introduction au développement pour smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1013"/>
		<updated>2011-12-02T00:00:19Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Sencha touch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Pour développer une application mobile, on a d&#039;abord un très grand embarras du [[aperçu de technologies mobiles|choix]]. &lt;br /&gt;
&lt;br /&gt;
Même en privilégiant les solutions multiplateformes, faciles, pas chères et open source il reste encore des candidats. Faute d&#039;avoir le temps de tout essayer je me suis décidé un peu arbitrairement pour [http://phonegap.com/ phonegap] et [http://www.sencha.com/products/touch/ sencha touch].&lt;br /&gt;
&lt;br /&gt;
Le cours a lieu sur des machines windows. Il y a plein de logiciels à [[Installation des outils pour smartphone|installer]].&lt;br /&gt;
&lt;br /&gt;
== Phonegap ==&lt;br /&gt;
Phonegap permet de créer des applications pour mobile en utilisant html/css/javascript, et donne accès à des fonctionnalités du téléphone auxquelles une simple page web n&#039;a pas accès.&lt;br /&gt;
&lt;br /&gt;
Pour voir comment utiliser ces fonctionnalités, nous commençons par examiner le [[phonegap sample project|projet livré en exemple avec phonegap]]&lt;br /&gt;
&lt;br /&gt;
== Sencha touch ==&lt;br /&gt;
On peut construire soi-même son interface graphique à la main avec html/css, mais c&#039;est plus pratique d&#039;utiliser une librairie de composants graphiques comme sencha touch. Nous créons avec sencha touch une [[sencha touch contacts|application qui affiche les contacts du téléphone]].&lt;br /&gt;
&lt;br /&gt;
Pour finir nous examinons une application dans un état plus avancé, mais qui est loin d&#039;être finie. L&#039;exercice constitue à décortiquer l&#039;application, et à la compléter:&lt;br /&gt;
# Mettre tout en haut la barre des taches qui est tout en bas.&lt;br /&gt;
# Ajouter une rangée de boutons&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# L&#039;application n&#039;est pas très belle, comment pourrait on l&#039;améliorer?&lt;br /&gt;
## Créer une meilleure template pour afficher les rangées.&lt;br /&gt;
## Peut-on remplacer les boutons par du css/html fait à la main? (Je n&#039;en suis pas sur. J&#039;aimerais bien avoir une espèce de bouton qui reste appuyé en fonction de la rangée sélectionnée.)&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1012</id>
		<title>Introduction au développement pour smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1012"/>
		<updated>2011-12-02T00:00:01Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Sencha touch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Pour développer une application mobile, on a d&#039;abord un très grand embarras du [[aperçu de technologies mobiles|choix]]. &lt;br /&gt;
&lt;br /&gt;
Même en privilégiant les solutions multiplateformes, faciles, pas chères et open source il reste encore des candidats. Faute d&#039;avoir le temps de tout essayer je me suis décidé un peu arbitrairement pour [http://phonegap.com/ phonegap] et [http://www.sencha.com/products/touch/ sencha touch].&lt;br /&gt;
&lt;br /&gt;
Le cours a lieu sur des machines windows. Il y a plein de logiciels à [[Installation des outils pour smartphone|installer]].&lt;br /&gt;
&lt;br /&gt;
== Phonegap ==&lt;br /&gt;
Phonegap permet de créer des applications pour mobile en utilisant html/css/javascript, et donne accès à des fonctionnalités du téléphone auxquelles une simple page web n&#039;a pas accès.&lt;br /&gt;
&lt;br /&gt;
Pour voir comment utiliser ces fonctionnalités, nous commençons par examiner le [[phonegap sample project|projet livré en exemple avec phonegap]]&lt;br /&gt;
&lt;br /&gt;
== Sencha touch ==&lt;br /&gt;
On peut construire soi-même son interface graphique à la main avec html/css, mais c&#039;est plus pratique d&#039;utiliser une librairie de composants graphiques comme sencha touch. Nous créons avec sencha touch une [[sencha touch contacts|application qui affiche les contacts du téléphone]].&lt;br /&gt;
&lt;br /&gt;
Pour finir nous examinons une application dans un état plus avancé, mais qui est loin d&#039;être finie. L&#039;exercice constitue à décortiquer l&#039;application, et à la compléter:&lt;br /&gt;
# Mettre tout en haut la barre des taches qui est tout en bas.&lt;br /&gt;
# Ajouter une rangée de boutons&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# L&#039;application n&#039;est pas très belle, comment pourrait on l&#039;améliorer?&lt;br /&gt;
## Créer une meilleure template pour afficher les rangées.&lt;br /&gt;
## Peut-on remplacer les boutons par du css/html fait à la main? (Je n&#039;en suis pas sur. J&#039;aimerais bien avoir une espèce de bouton qui reste appuyé en fonction de la rangée sélectionnée.)&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1011</id>
		<title>Introduction au développement pour smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1011"/>
		<updated>2011-12-01T23:59:30Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Sencha touch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Pour développer une application mobile, on a d&#039;abord un très grand embarras du [[aperçu de technologies mobiles|choix]]. &lt;br /&gt;
&lt;br /&gt;
Même en privilégiant les solutions multiplateformes, faciles, pas chères et open source il reste encore des candidats. Faute d&#039;avoir le temps de tout essayer je me suis décidé un peu arbitrairement pour [http://phonegap.com/ phonegap] et [http://www.sencha.com/products/touch/ sencha touch].&lt;br /&gt;
&lt;br /&gt;
Le cours a lieu sur des machines windows. Il y a plein de logiciels à [[Installation des outils pour smartphone|installer]].&lt;br /&gt;
&lt;br /&gt;
== Phonegap ==&lt;br /&gt;
Phonegap permet de créer des applications pour mobile en utilisant html/css/javascript, et donne accès à des fonctionnalités du téléphone auxquelles une simple page web n&#039;a pas accès.&lt;br /&gt;
&lt;br /&gt;
Pour voir comment utiliser ces fonctionnalités, nous commençons par examiner le [[phonegap sample project|projet livré en exemple avec phonegap]]&lt;br /&gt;
&lt;br /&gt;
== Sencha touch ==&lt;br /&gt;
On peut construire soi-même son interface graphique à la main avec html/css, mais c&#039;est plus pratique d&#039;utiliser une librairie de composants graphiques comme sencha touch. Nous créons avec sencha touch une [[sencha touch contacts|application qui affiche les contacts du téléphone]].&lt;br /&gt;
&lt;br /&gt;
Pour finir nous examinons une application dans un état plus avancé, mais qui est loin d&#039;être finie. L&#039;exercice constitue à décortiquer l&#039;application, et à la compléter:&lt;br /&gt;
# Mettre tout en haut la barre des taches qui est tout en bas.&lt;br /&gt;
# Ajouter une rangée de boutons&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
reste appuyé en fonction de la rangée sélectionnée.&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;br /&gt;
# L&#039;application n&#039;est pas très belle, comment pourrait on l&#039;améliorer?&lt;br /&gt;
## Créer une meilleure template pour afficher les rangées.&lt;br /&gt;
## Peut-on remplacer les boutons par du css/html fait à la main? (Je n&#039;en suis pas sur. J&#039;aimerais bien avoir une espèce de bouton qui&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1010</id>
		<title>Introduction au développement pour smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1010"/>
		<updated>2011-12-01T23:59:06Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Sencha touch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Pour développer une application mobile, on a d&#039;abord un très grand embarras du [[aperçu de technologies mobiles|choix]]. &lt;br /&gt;
&lt;br /&gt;
Même en privilégiant les solutions multiplateformes, faciles, pas chères et open source il reste encore des candidats. Faute d&#039;avoir le temps de tout essayer je me suis décidé un peu arbitrairement pour [http://phonegap.com/ phonegap] et [http://www.sencha.com/products/touch/ sencha touch].&lt;br /&gt;
&lt;br /&gt;
Le cours a lieu sur des machines windows. Il y a plein de logiciels à [[Installation des outils pour smartphone|installer]].&lt;br /&gt;
&lt;br /&gt;
== Phonegap ==&lt;br /&gt;
Phonegap permet de créer des applications pour mobile en utilisant html/css/javascript, et donne accès à des fonctionnalités du téléphone auxquelles une simple page web n&#039;a pas accès.&lt;br /&gt;
&lt;br /&gt;
Pour voir comment utiliser ces fonctionnalités, nous commençons par examiner le [[phonegap sample project|projet livré en exemple avec phonegap]]&lt;br /&gt;
&lt;br /&gt;
== Sencha touch ==&lt;br /&gt;
On peut construire soi-même son interface graphique à la main avec html/css, mais c&#039;est plus pratique d&#039;utiliser une librairie de composants graphiques comme sencha touch. Nous créons avec sencha touch une [[sencha touch contacts|application qui affiche les contacts du téléphone]].&lt;br /&gt;
&lt;br /&gt;
Pour finir nous examinons une application dans un état plus avancé, mais qui est loin d&#039;être finie. L&#039;exercice constitue à décortiquer l&#039;application, et à la compléter:&lt;br /&gt;
# Mettre tout en haut la barre des taches qui est tout en bas.&lt;br /&gt;
# Ajouter une rangée de boutons&lt;br /&gt;
# Désactiver la fonctionnalité de chaque bouton et la remplacer par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
# L&#039;application n&#039;est pas très belle, comment pourrait on l&#039;améliorer?&lt;br /&gt;
## Créer une meilleure template pour afficher les rangées.&lt;br /&gt;
## Peut-on remplacer les boutons par du css/html fait à la main? (Je n&#039;en suis pas sur. J&#039;aimerais bien avoir une espèce de bouton qui reste appuyé en fonction de la rangée sélectionnée.&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1009</id>
		<title>Introduction au développement pour smartphone</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Introduction_au_d%C3%A9veloppement_pour_smartphone&amp;diff=1009"/>
		<updated>2011-12-01T23:58:24Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* Sencha touch */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Pour développer une application mobile, on a d&#039;abord un très grand embarras du [[aperçu de technologies mobiles|choix]]. &lt;br /&gt;
&lt;br /&gt;
Même en privilégiant les solutions multiplateformes, faciles, pas chères et open source il reste encore des candidats. Faute d&#039;avoir le temps de tout essayer je me suis décidé un peu arbitrairement pour [http://phonegap.com/ phonegap] et [http://www.sencha.com/products/touch/ sencha touch].&lt;br /&gt;
&lt;br /&gt;
Le cours a lieu sur des machines windows. Il y a plein de logiciels à [[Installation des outils pour smartphone|installer]].&lt;br /&gt;
&lt;br /&gt;
== Phonegap ==&lt;br /&gt;
Phonegap permet de créer des applications pour mobile en utilisant html/css/javascript, et donne accès à des fonctionnalités du téléphone auxquelles une simple page web n&#039;a pas accès.&lt;br /&gt;
&lt;br /&gt;
Pour voir comment utiliser ces fonctionnalités, nous commençons par examiner le [[phonegap sample project|projet livré en exemple avec phonegap]]&lt;br /&gt;
&lt;br /&gt;
== Sencha touch ==&lt;br /&gt;
On peut construire soi-même son interface graphique à la main avec html/css, mais c&#039;est plus pratique d&#039;utiliser une librairie de composants graphiques comme sencha touch. Nous créons avec sencha touch une [[sencha touch contacts|application qui affiche les contacts du téléphone]].&lt;br /&gt;
&lt;br /&gt;
Pour finir nous examinons une application dans un état plus avancé, mais qui est loin d&#039;être finie. L&#039;exercice constitue à décortiquer l&#039;application, et à la compléter:&lt;br /&gt;
# Mettre tout en haut la barre des taches qui est tout en bas.&lt;br /&gt;
# Ajouter une rangée de boutons&lt;br /&gt;
# Remplacer la fonctionnalité de chaque boutons par un alert.&lt;br /&gt;
# Voir comment fonctionne l&#039;enregistrement de données.&lt;br /&gt;
# Voir comment l&#039;application envoie des données ailleurs par ajax.&lt;br /&gt;
# L&#039;application n&#039;est pas très belle, comment pourrait on l&#039;améliorer?&lt;br /&gt;
## Créer une meilleure template pour afficher les rangées.&lt;br /&gt;
## Peut-on remplacer les boutons par du css/html fait à la main? (Je n&#039;en suis pas sur. J&#039;aimerais bien avoir une espèce de bouton qui reste appuyé en fonction de la rangée sélectionnée.&lt;br /&gt;
# L&#039;application ne fonctionne que pour un mois de l&#039;année. Comment peut-on laisser l&#039;utilisateur choisir le mois&lt;br /&gt;
## Choisir un composant graphique&lt;br /&gt;
## Ajouter un bouton qui appelle ce composant graphique&lt;br /&gt;
## Gérer l&#039;affichage en fonction du mois&lt;br /&gt;
## Gérer la nouvelle donnée du mois pour l&#039;enregistrement et l&#039;envoi par ajax.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1008</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1008"/>
		<updated>2011-12-01T23:48:27Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exercice */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(application0.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js,&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
(application1.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;pre&amp;gt;  getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;pre&amp;gt;  getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# Trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# Trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;pre&amp;gt;   &lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup.(application2.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Déployer et voir l&#039;alerte&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application3.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;pre&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1007</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1007"/>
		<updated>2011-12-01T23:47:54Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* exercices */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(application0.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js,&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
(application1.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;pre&amp;gt;  getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;pre&amp;gt;  getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# Trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# Trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;pre&amp;gt;   &lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup.(application2.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Déployer et voir l&#039;alerte&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application3.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;pre&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== exercice ==&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1006</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1006"/>
		<updated>2011-12-01T23:46:20Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* déboguer sur le téléphone */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(application0.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier servage.html sur le serveur&lt;br /&gt;
# dans la console:&lt;br /&gt;
#* cd h:/weinre-jar&lt;br /&gt;
#* java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js,&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
(application1.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;pre&amp;gt;  getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;pre&amp;gt;  getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Mettre ça sur le serveur et le regarder avec le téléphone.&lt;br /&gt;
# supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;pre&amp;gt;   &lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup.(application2.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Déployer et voir l&#039;alerte&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application3.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;pre&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== exercice ==&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
	<entry>
		<id>https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1005</id>
		<title>Sencha touch contacts</title>
		<link rel="alternate" type="text/html" href="https://wikilipo.unige.ch/w/index.php?title=Sencha_touch_contacts&amp;diff=1005"/>
		<updated>2011-12-01T23:45:22Z</updated>

		<summary type="html">&lt;p&gt;Andreaskundig : /* déboguer sur le téléphone */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Il s&#039;agit de représenter les contacts du téléphone avec les composants graphiques de sencha touch.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== demo ==&lt;br /&gt;
&lt;br /&gt;
Pour se faire une idée de ce qu&#039;on peut faire avec sencha touch, c&#039;est bien d&#039;aller voir les demos sur leur site:&lt;br /&gt;
http://dev.sencha.com/deploy/touch/examples/&lt;br /&gt;
&lt;br /&gt;
Les demos sont aussi comprises dans la distribution de sencha qu&#039;on peut télécharger ici: http://www.sencha.com/products/touch/download&lt;br /&gt;
Déballer sencha-touch-1.1.0.zip dans h:/work, et ouvrir les démos dans chrome: file:///h:/work/sencha-touch-1.1.0/examples/index.html&lt;br /&gt;
&lt;br /&gt;
Voir le kitchen sink qui est une demonstration de la plupart des composants graphiques. L&#039;exemple sous User Interface &amp;gt; List serait approprié &lt;br /&gt;
&lt;br /&gt;
Pour examiner le code source des exemples, et tout le reste de sencha, c&#039;est bien de créer un projet sencha dans eclipse et d&#039;importer tous les fichiers sous h:/work/sencha-touch-1.1.0&lt;br /&gt;
&lt;br /&gt;
Il y a aussi beaucoup à apprendre dans la documentation en ligne: http://docs.sencha.com/touch/1-1/&lt;br /&gt;
&lt;br /&gt;
== ossature du projet ==&lt;br /&gt;
&lt;br /&gt;
créer un nouveau projet senchagui&lt;br /&gt;
créer les répertoires assets/www/js&lt;br /&gt;
&lt;br /&gt;
créer assets/www/lib/touch&lt;br /&gt;
y copier les fichiers du répertoire c:/work/sencha-touch-1.1.0&lt;br /&gt;
ainsi que les sous-répertoires pkgs, resources, src&lt;br /&gt;
!! au déploiement faudra garder que le js et css nécessaire&lt;br /&gt;
&lt;br /&gt;
créer assets/www/index.html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
 &amp;lt;html&amp;gt;&lt;br /&gt;
 &amp;lt;head&amp;gt;&lt;br /&gt;
   &amp;lt;title&amp;gt;Learning sencha&amp;lt;/title&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/touch/sencha-touch-debug-w-comments.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;js/application.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;/body&amp;gt;&lt;br /&gt;
&amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pendant le développement, on utilise la version lisible de la librairie sencha-touch-debug-w-comments.js. &lt;br /&gt;
&lt;br /&gt;
créer assets/www/js/application.js &lt;br /&gt;
(application0.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;une barre&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selon la doc Ext.setup reçoit un objet avec des attributs;&lt;br /&gt;
* l&#039;attribut onReady est une fonction qui va être appelée quand la page est chargée&lt;br /&gt;
&lt;br /&gt;
Ext.Panel&lt;br /&gt;
* il y a deux manières de mettre le html&lt;br /&gt;
* on voit un exemple ou la toolbar est définie d&#039;une autre manière&lt;br /&gt;
* une recherche sur xtype mène au tableau de correspondance entre xtype et classes: http://docs.sencha.com/touch/1-1/#!/api/Ext.Component&lt;br /&gt;
* dockedItem est un composant qu&#039;on peut coller au bord du panel. Il a un attribut dock qui peut être top, right, left ou bottom&lt;br /&gt;
&lt;br /&gt;
on peut ouvrir ça dans chrome&lt;br /&gt;
&lt;br /&gt;
Pour y accéder depuis le téléphone, on peut mettre le projet sur infolipo.net avec le client ftp filezilla.&lt;br /&gt;
L&#039;adresse du serveur est &#039;&#039;&#039;ftp.servage.net&#039;&#039;&#039;, l&#039;utilisateur est &#039;&#039;&#039;smartcours&#039;&#039;&#039; et je vais vous donner le mot de passe.&lt;br /&gt;
&lt;br /&gt;
* Créer un répertoire à son nom (le mien s&#039;appelle andreas)&lt;br /&gt;
* Sencha est déjà sur le serveur, pour éviter que chacun doive le copier dans son projet.&lt;br /&gt;
Pour l&#039;inclure dans l&#039;application, il faut faire une version modifiée d&#039;index.html, &lt;br /&gt;
qu&#039;on peut appeler servage.html et dans lequel on change l&#039;adresse des fichiers sencha&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;../lib/touch/sencha-touch.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
   &amp;lt;link href=&amp;quot;../lib/touch/resources/css/sencha-touch.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Déployer l&#039;application sur le serveur, y accéder par téléphone http://infolipo.net/cours/[votre nom]/servage.html&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== exercice ===&lt;br /&gt;
# changer la couleur de fond de l&#039;application&lt;br /&gt;
&lt;br /&gt;
=== déboguer sur le téléphone ===&lt;br /&gt;
# démarrer une console dos, tape ipconfig et noter l&#039;adresse ip du pc&lt;br /&gt;
# ajouter ceci dans servage.html: &amp;lt;script src=&amp;quot;http://[adresse ip]:8081/target/target-script-min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
# copier index.html sur le serveur&lt;br /&gt;
# cd h:/weinre-jar&lt;br /&gt;
# java -jar weinre.jar --boundHost [adresse ip] --httpPort 8081&lt;br /&gt;
# aller sur http://infolipo.net/cours/[votre nom]/servage.html avec le téléphone&lt;br /&gt;
# dans chrome aller sur http://localhost:8081 et cliquer sur client&lt;br /&gt;
&lt;br /&gt;
== créer une liste ==&lt;br /&gt;
&lt;br /&gt;
Le code de la demo kitchensink est un peu compliqué, &lt;br /&gt;
mais on peut chercher List dans la doc.&lt;br /&gt;
On peut carrément copier l&#039;exemple donné dans application.js,&lt;br /&gt;
et ajouter la liste dans les items du panel:&lt;br /&gt;
(application1.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({&lt;br /&gt;
      onReady: function() {&lt;br /&gt;
&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Spencer&#039;}&lt;br /&gt;
           //...&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         indexBar: true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            //html: &amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;,&lt;br /&gt;
            //items: [{html:&amp;quot;toute l&#039;application va être à l&#039;intérieur de ce &amp;lt;b&amp;gt;panneau&amp;lt;/b&amp;gt;&amp;quot;}],&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 }});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que la liste contient un &#039;&#039;&#039;store&#039;&#039;&#039;, qui utilise un modèle, &lt;br /&gt;
qui est défini par Ext.regModel, qui crée un objet Ext.data.Model&lt;br /&gt;
http://docs.sencha.com/touch/1-1/#!/api/Ext.data.Model&lt;br /&gt;
&lt;br /&gt;
Un tour dans la doc montre qu&#039;un modèle offre toute une série de fonctionnalités:&lt;br /&gt;
* la validation permet de vérifier que les champs contiennent des valeurs correctes.&lt;br /&gt;
* on peut les associer à d&#039;autres modèles&lt;br /&gt;
* on peut les enregistrer de différentes manières&lt;br /&gt;
Toutes ces fonctionnalités vont au-delà du coté graphique pour lequel nous avons choisi sencha touch.&lt;br /&gt;
&lt;br /&gt;
=== model / view / controller ===&lt;br /&gt;
Au delà des composants graphiques, sencha offre un framework pour construire des applications.&lt;br /&gt;
Le framework offre des fonctionnalités que dont les applications ont en général besoin,&lt;br /&gt;
et il offre aussi une manière d&#039;organiser le code, qui suit un principe d&#039;architecture connu:&lt;br /&gt;
le pattern Model/View/Controller. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&lt;br /&gt;
&lt;br /&gt;
;Les modèles : représentent les données, par exemple les contacts que nous avons défini par les champs firstName et lastName.&lt;br /&gt;
;Les vues : servent à représenter les données graphiquement. Si on suit la structure voulue par Sencha, il faut créer des objets View à base de composants graphiques comme Ext.Panel, Ext.List etc..&lt;br /&gt;
;Les controllers : font le lien entre les vues et les modèles. Un bouton, qui fait partie de la vue, appelle un controller quand il est appuyé, et le controleur apporte des modifications au modèle. Souvent ce n&#039;est pas tout à fait évident de dire ou s&#039;arrete la responsabilité du modele ou de la vue et ou commence le controller.&lt;br /&gt;
&lt;br /&gt;
C&#039;est tout à fait légitime d&#039;ignorer tout celà si on en a pas besoin, et de continuer à se servir juste des composants graphiques. Les questions d&#039;architecture deviennent plus importantes pour des plus grosses applications.&lt;br /&gt;
&lt;br /&gt;
=== configuration de List ===&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;itemTpl&#039;&#039;&#039; est une template.&lt;br /&gt;
*  Chaque élément de la liste va être représenté par ce texte, ou les variables &#039;&#039;&#039;{firstName}&#039;&#039;&#039; et &#039;&#039;&#039;{lastName}&#039;&#039;&#039; sont remplacées par leur valeur dans le modèle représenté.&lt;br /&gt;
*  On peut ajouter du texte à la template:&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;Prénom: {firstName}, Nom: {lastName}&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  On peut même ajouter des éléments html&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;Prénom: {firstName}, &amp;lt;b&amp;gt;Nom&amp;lt;/b&amp;gt;: &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;{lastName}&amp;lt;/span&amp;gt;&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  La documentation d&#039;itemTpl mentionne qu&#039;elle suit une structure de XTemplate. En cherchant XTemplate on trouve la documentation sur le langage de template. Dans les exemples on voit que la variable {#} indique le numéro de l&#039;élément.&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName}&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
:*  On voit qu&#039;on peut tester des conditions&lt;br /&gt;
&amp;lt;pre&amp;gt;   itemTpl : &#039;{#}. {firstName} {lastName} &amp;lt;tpl if=&amp;quot;firstName[0]==\&#039;T\&#039;&amp;quot;&amp;gt; (commence par T)&amp;lt;/tpl&amp;gt;&#039;,&amp;lt;/pre&amp;gt;&lt;br /&gt;
(Ici, il a fallu écrire \&#039; pour éviter que les guillemets ne se ferment)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;grouped&#039;&#039;&#039; indique si les éléments doivent être groupés. Si on veut grouper les éléments,il faut donner au store une méthode getGroupString qui retourne le string qui sert à grouper les éléments. ici on a &lt;br /&gt;
&amp;lt;pre&amp;gt;  getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pour grouper les éléments par la deuxième lettre du prénom, on peut écrire&lt;br /&gt;
&amp;lt;pre&amp;gt;  getGroupString : function(record) { return record.get(&#039;firstName&#039;)[1];},&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== exercices ===&lt;br /&gt;
# Essayer les différentes manières de faire des templates, et examiner le code généré dans chrome. [clef à molette] &amp;gt; Outils &amp;gt; Outils de développement (ou directement ctrl+maj+I)&lt;br /&gt;
# Mettre ça sur le serveur et le regarder avec le téléphone.&lt;br /&gt;
# supprimer la liste alphabétique qui apparait à droite&lt;br /&gt;
# trouver comment ajouter le bouton à droite des éléments qui apparait dans l&#039;exemple List du kitchensink quand on clique sur Disclosure&lt;br /&gt;
# trouver comment executer du code quand le bouton est appuyé.&lt;br /&gt;
pistes: regarder la doc sencha, le code des exemples livrés avec sencha&lt;br /&gt;
&lt;br /&gt;
== intégrer les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
=== ossature du projet phonegap ===&lt;br /&gt;
&lt;br /&gt;
on suit les instructions de http://phonegap.com/start/#android&lt;br /&gt;
&lt;br /&gt;
Il faut créer un nouveau projet qui intègre phonegap.&lt;br /&gt;
* File &amp;gt; new &amp;gt; other &amp;gt; Android project&lt;br /&gt;
* Project name: senchacontacts&lt;br /&gt;
* Build Target: Android 4.0&lt;br /&gt;
* Package Name: org.infolipo&lt;br /&gt;
* new folders: libs assets/www&lt;br /&gt;
* copier depuis callback-phonegap-9f0856d\Android&lt;br /&gt;
** phonegap-1.2.0.jar vers libs&lt;br /&gt;
** phonegap-1.2.0.js vers assets/www&lt;br /&gt;
** xml vers res&lt;br /&gt;
Retour dans eclipse&lt;br /&gt;
*  click droit senchacontacts &amp;gt; refresh&lt;br /&gt;
*  click droit sur libs/phonegap-1.2.0.jar &amp;gt; Build path &amp;gt; Add to build path&lt;br /&gt;
*  modifier src/org.infolipo/SenchacontactsActivity.java&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public class SenchacontactsActivity extends DroidGap { //Activity {&lt;br /&gt;
    super.loadUrl(&amp;quot;file:///android_asset/www/index.html&amp;quot;); //setContentView(R.layout.main);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Source &amp;gt; Organize imports (ou ctrl-shift-o)&lt;br /&gt;
* click droit sur AndroidManifest.xml &amp;gt; open with &amp;gt; text editor&lt;br /&gt;
* insérer ceci après versionname&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;supports-screens&lt;br /&gt;
   android:largeScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:normalScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:smallScreens=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:resizeable=&amp;quot;true&amp;quot;&lt;br /&gt;
   android:anyDensity=&amp;quot;true&amp;quot;&lt;br /&gt;
   /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.CAMERA&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.VIBRATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_COARSE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_FINE_LOCATION&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_LOCATION_EXTRA_COMMANDS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_PHONE_STATE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.INTERNET&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECEIVE_SMS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.RECORD_AUDIO&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.MODIFY_AUDIO_SETTINGS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.READ_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_CONTACTS&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.WRITE_EXTERNAL_STORAGE&amp;quot; /&amp;gt;&lt;br /&gt;
   &amp;lt;uses-permission android:name=&amp;quot;android.permission.ACCESS_NETWORK_STATE&amp;quot; /&amp;gt; &amp;lt;uses-permission android:name=&amp;quot;android.permission.GET_ACCOUNTS&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ajouter &#039;&#039;android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&#039;&#039; dans&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;activity&lt;br /&gt;
            android:label=&amp;quot;@string/app_name&amp;quot;&lt;br /&gt;
            android:name=&amp;quot;.SenchacontactsActivity&amp;quot; &lt;br /&gt;
            android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* ajouter une deuxieme activité après la première&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;activity android:name=&amp;quot;com.phonegap.DroidGap&amp;quot; &lt;br /&gt;
                  android:label=&amp;quot;@string/app_name&amp;quot; &lt;br /&gt;
                  android:configChanges=&amp;quot;orientation|keyboardHidden&amp;quot;&amp;gt; &lt;br /&gt;
           &amp;lt;intent-filter&amp;gt; &amp;lt;/intent-filter&amp;gt;&lt;br /&gt;
        &amp;lt;/activity&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* changer la version du sdk pour correspondre au téléphone (sur le téléphone Réglages &amp;gt; A propos du téléphone, Version Android)    http://developer.android.com/guide/appendix/api-levels.html&lt;br /&gt;
&amp;lt;pre&amp;gt;   &lt;br /&gt;
    &amp;lt;uses-sdk android:minSdkVersion=&amp;quot;10&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* copier le répertoire assets du projet précédent (senchagui)&lt;br /&gt;
* inclure la librairie phonegap dans index.html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;lib/phonegap-1.2.0.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* effacer servage.html: on ne peut pas mettre une application phonegap sur un serveur.&lt;br /&gt;
&lt;br /&gt;
== initialisation de l&#039;application ==&lt;br /&gt;
&lt;br /&gt;
Avant de commencer à travailler, il faut s&#039;assurer à la fois que phonegap et que sencha sont bien initialisés. Phonegap est initialisé quand quand l&#039;événement deviceReady est lancé. On peut s&#039;y abonner en y ajoutant cette ligne dans &#039;&#039;&#039;index.html&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
      document.addEventListener(&amp;quot;deviceready&amp;quot;, initializer.onPhonegapReady, false);&lt;br /&gt;
   &amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ainsi l&#039;événement deviceReady déclenche l&#039;appel de la fonction initializer.onPhonegapReady. On définit la fonction onPhonegapReady sur un nouvel objet initializer dans &#039;&#039;&#039;application.js&#039;&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ initializer.phonegapReady = true; },&lt;br /&gt;
	onSenchaReady: function(){ initializer.senchaReady = true; }&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La méthode &#039;&#039;&#039;onSenchaReady&#039;&#039;&#039; sert à être appelée quand sencha est prêt. On l&#039;appelle depuis la fonction &#039;&#039;&#039;onReady&#039;&#039;&#039; de la configuration passée en argument au setup.(application2.js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On rajoute à l&#039;initializer une méthode appelée quand phonegap et sencha sont prêts&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){ &lt;br /&gt;
	   initializer.phonegapReady = true;&lt;br /&gt;
	   if(initializer.senchaReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onSenchaReady: function(){&lt;br /&gt;
           initializer.senchaReady = true;&lt;br /&gt;
           if(initializer.phonegapReady){ initializer.onEverythingReady(); }&lt;br /&gt;
	},&lt;br /&gt;
	onEverythingReady: function(){&lt;br /&gt;
		alert(&amp;quot;phonegapReady: &amp;quot; + initializer.phonegapReady + &amp;quot;\nsenchaReady:&amp;quot; + initializer.senchaReady);&lt;br /&gt;
	}	&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Déployer et voir l&#039;alerte&lt;br /&gt;
&lt;br /&gt;
On peut maintenant rajouter dans &#039;&#039;&#039;onEverythingReady&#039;&#039;&#039; le code pour créer la liste.(application3.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 Ext.setup({ onReady: function() { initializer.onSenchaReady();}});&lt;br /&gt;
&lt;br /&gt;
 var initializer = {&lt;br /&gt;
 	onPhonegapReady: function(){&lt;br /&gt;
 		initializer.phonegapReady = true;&lt;br /&gt;
 		if (initializer.senchaReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	},&lt;br /&gt;
 	onSenchaReady: function(){&lt;br /&gt;
 		initializer.senchaReady = true;&lt;br /&gt;
 		if (initializer.phonegapReady){ initializer.onEverythingReady();}&lt;br /&gt;
 	}&lt;br /&gt;
  };&lt;br /&gt;
 &lt;br /&gt;
 // j&#039;ai sorti de l&#039;initializer la définition de la fonction qui lance tout&lt;br /&gt;
 // pour bien la mettre en évidence&lt;br /&gt;
 initializer.onEverythingReady = function(){&lt;br /&gt;
		&lt;br /&gt;
       Ext.regModel(&#039;Contact&#039;, {fields: [&#039;firstName&#039;, &#039;lastName&#039;]} );&lt;br /&gt;
&lt;br /&gt;
       var store = new Ext.data.JsonStore({&lt;br /&gt;
         model  : &#039;Contact&#039;,&lt;br /&gt;
         sorters: &#039;lastName&#039;,&lt;br /&gt;
         getGroupString : function(record) { return record.get(&#039;lastName&#039;)[0];},&lt;br /&gt;
         data: [&lt;br /&gt;
           {firstName: &#039;Tommy&#039;,   lastName: &#039;Maintz&#039;},&lt;br /&gt;
           {firstName: &#039;Rob&#039;,     lastName: &#039;Dougan&#039;},&lt;br /&gt;
           {firstName: &#039;Ed&#039;,      lastName: &#039;Mencer&#039;}&lt;br /&gt;
         ]&lt;br /&gt;
       });&lt;br /&gt;
&lt;br /&gt;
       var list = new Ext.List({&lt;br /&gt;
         fullscreen: true, &lt;br /&gt;
         itemTpl : &#039;{firstName} {lastName}&#039;,&lt;br /&gt;
         indexBar:true,&lt;br /&gt;
         grouped : true,&lt;br /&gt;
         store: store});      &lt;br /&gt;
&lt;br /&gt;
       var toolbar = new Ext.Toolbar({dock: &#039;top&#039;,title: &#039;Contacts&#039; });&lt;br /&gt;
       new Ext.Panel({&lt;br /&gt;
            fullscreen: true,&lt;br /&gt;
            items: [list],&lt;br /&gt;
            dockedItems: [toolbar]&lt;br /&gt;
       });&lt;br /&gt;
 };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== lire les contacts du téléphone ==&lt;br /&gt;
&lt;br /&gt;
On peut s&#039;inspirer du projet exemple que nous avons vu en premier, ou de la documentation phonegap. Pour contacts.find elle donne l&#039;exemple suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // PhoneGap is ready&lt;br /&gt;
    //&lt;br /&gt;
    function onDeviceReady() {&lt;br /&gt;
        // find all contacts with &#039;Bob&#039; in any name field&lt;br /&gt;
        var options = new ContactFindOptions();&lt;br /&gt;
        options.filter=&amp;quot;Bob&amp;quot;; &lt;br /&gt;
        var fields = [&amp;quot;displayName&amp;quot;, &amp;quot;name&amp;quot;];&lt;br /&gt;
        navigator.contacts.find(fields, onSuccess, onError, options);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onSuccess: Get a snapshot of the current contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onSuccess(contacts) {&lt;br /&gt;
        for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
            console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // onError: Failed to get the contacts&lt;br /&gt;
    //&lt;br /&gt;
    function onError(contactError) {&lt;br /&gt;
        alert(&#039;onError!&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il s&#039;agit d&#039;appeler &lt;br /&gt;
&amp;lt;pre&amp;gt; navigator.contacts.find(fields, onSuccess, onError, options); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;fields : contient les noms des champs de Contact à retourner. La doc de Contact nous donne le nom des champs disponibles.&lt;br /&gt;
;onSuccess et onError : sont les fonctions à appeler après l&#039;execution.&lt;br /&gt;
;options: Les options servent à préciser quels contacts on cherche. Elles sont facultatives.&lt;br /&gt;
&lt;br /&gt;
Pour commencer simplement, nous allons juste afficher tous les noms sur la console. Ajouter ceci à la fin de onEverythingReady (application4.js):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      function findSuccess(contacts){&lt;br /&gt;
              for (var i=0; i&amp;lt;contacts.length; i++) {&lt;br /&gt;
                console.log(&amp;quot;Display Name = &amp;quot; + contacts[i].displayName);&lt;br /&gt;
              }&lt;br /&gt;
      };&lt;br /&gt;
		   &lt;br /&gt;
      navigator.contacts.find(&lt;br /&gt;
           [&amp;quot;displayName&amp;quot;], &lt;br /&gt;
           findSuccess,&lt;br /&gt;
           function(error){ console.log(&amp;quot;error: &amp;quot; + error) }&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regarder l&#039;application dans l&#039;émulateur. Ouvrir la console dans chrome: Outils &amp;gt; Console Javascript. Vérifier que les noms sont affichés.&lt;br /&gt;
&lt;br /&gt;
Charger l&#039;application sur le téléphone,les noms devraient s&#039;afficher dans logCat. Mettre un filtre dans logCat en cliquant sur + &lt;br /&gt;
et en mettant &amp;quot;Web Console&amp;quot; dans le champ &amp;quot;by Log Tag&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== exercice ==&lt;br /&gt;
&lt;br /&gt;
Nous savons afficher des contacts, et nous savons lire les contacts du téléphone. Il faut maintenant remplacer les données fictives définies dans l&#039;objet store par les contacts du téléphone. Il est suffisant d&#039;afficher juste le champ displayName.&lt;/div&gt;</summary>
		<author><name>Andreaskundig</name></author>
	</entry>
</feed>