<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Leo Tech Blog</title>
	<atom:link href="http://leo.cacheux.net/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://leo.cacheux.net/blog</link>
	<description>PHP, Android, Linux and more</description>
	<lastBuildDate>Fri, 27 Jan 2012 22:26:32 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4.2</generator>
		<item>
		<title>Ice Cream Sandwich : l&#8217;article et la conférence</title>
		<link>http://leo.cacheux.net/blog/2012/01/21/ice-cream-sandwich-larticle-et-la-conference/</link>
		<comments>http://leo.cacheux.net/blog/2012/01/21/ice-cream-sandwich-larticle-et-la-conference/#comments</comments>
		<pubDate>Sat, 21 Jan 2012 10:07:25 +0000</pubDate>
		<dc:creator>Léo</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[paug]]></category>
		<category><![CDATA[programmez]]></category>

		<guid isPermaLink="false">http://leo.cacheux.net/blog/?p=87</guid>
		<description><![CDATA[Léo, ou l&#8217;art de donner les informations quand c&#8217;est trop tard. Mais tant pis. Je n&#8217;ai pas encore de téléphone ou de tablette sous Ice Cream Sandwich, mais j&#8217;ai quand même eu l&#8217;occasion de voir ce qui se cachait derrière. &#8230;<p class="read-more"><a href="http://leo.cacheux.net/blog/2012/01/21/ice-cream-sandwich-larticle-et-la-conference/">Lire la suite &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Léo, ou l&rsquo;art de donner les informations quand c&rsquo;est trop tard. Mais tant pis.</p>
<p style="text-align: justify;">Je n&rsquo;ai pas encore de téléphone ou de tablette sous Ice Cream Sandwich, mais j&rsquo;ai quand même eu l&rsquo;occasion de voir ce qui se cachait derrière. Une première fois en écrivant un article pour le magazine Programmez. 6 pages co-écrites avec trois collègues Genymobiliens, pour détailler toutes les nouveautés de l&rsquo;API level 14, depuis les nouveautés pour les calendriers et les contacts, jusqu&rsquo;à la caméra et au WiFi direct. Rien de très nouveau par rapport aux <a href="http://developer.android.com/sdk/android-4.0.html">platform highlights</a> du site officiel, si ce n&rsquo;est une traduction française et quelques exemples. Pas facile d&rsquo;ailleurs de tester tout cela sur émulateur uniquement, surtout qu&rsquo;au moment de la rédaction, il n&rsquo;y avait ni Nexus S mis à jour, ni Galaxy Nexus pour voir ce que cela pouvait donner en vrai.</p>
<p style="text-align: justify;">Suite à l&rsquo;article, c&rsquo;est une conférence donnée pour le <a href="http://www.paug.fr">Paris Android User Group</a> qui a été l&rsquo;occasion de se replonger dans l&rsquo;API. On prend les mêmes et on recommence, on y ajoute quelques nouveautés de l&rsquo;API level 15, quelques exemples supplémentaires, et voilà ma toute première conférence devant plus d&rsquo;une centaine de personnes, le <a href="http://www.meetup.com/Android-Paris/events/46922312/">19 janvier à l&rsquo;ECE</a>. Le stress n&rsquo;a pas vraiment aidé à rendre la conférence très dynamique, et j&rsquo;aurais aimé pouvoir présenter des exemples un peu plus sexys que des GridLayout, mais j&rsquo;étais toujours limité par l&rsquo;émulateur. Pas de WiFi direct, pas de NFC, et pas d&rsquo;OpenGL donc. Les critiques sur la conférence regrettent aussi que je n&rsquo;ai pas trop approfondi les sujets, mais j&rsquo;ai voulu faire quelque chose de très général et exhaustif, difficile donc de rentrer dans le détail de chaque sujet. Mais promis, je ferais mieux la prochaine fois ! <a href="http://www.paug.fr/conference-paug/slides-et-video-android-4-aosp-par-leo-cacheux-et-daniel-fages/">La conférence est à regarder ici</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://leo.cacheux.net/blog/2012/01/21/ice-cream-sandwich-larticle-et-la-conference/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sound Boost : Increase the volume of your Android device</title>
		<link>http://leo.cacheux.net/blog/2011/10/05/sound-boost-augmentez-le-volume-de-votre-appareil-android/</link>
		<comments>http://leo.cacheux.net/blog/2011/10/05/sound-boost-augmentez-le-volume-de-votre-appareil-android/#comments</comments>
		<pubDate>Tue, 04 Oct 2011 22:41:15 +0000</pubDate>
		<dc:creator>Léo</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[android market]]></category>
		<category><![CDATA[sound boost]]></category>

		<guid isPermaLink="false">http://leo.cacheux.net/blog/?p=35</guid>
		<description><![CDATA[Sometimes, I listen to audio podcasts in the subway. But with all the noise around me, it&#8217;s not easy to hear the talk, and I missed most of it, despite the volume is set to the max. Some applications were &#8230;<p class="read-more"><a href="http://leo.cacheux.net/blog/2011/10/05/sound-boost-augmentez-le-volume-de-votre-appareil-android/">Lire la suite &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<div class='en' style='' lang='en' dir='ltr'>
Sometimes, I listen to audio podcasts in the subway. But with all the noise around me, it&rsquo;s not easy to hear the talk, and I missed most of it, despite the volume is set to the max. Some applications were already available on the Android Market to push the volume above the limit, but they aren&rsquo;t free. I had two choices : pay a few cents, or develop my own application. As this solution was cleary simple, I choosed to do it on my own, so I can also offer a free application for everyone, <del>then become rich and famous</del>.</p>
<p>A few hours where necessary to find the miracle solution, then a few more hours to adjust the details, and finally a few days to create an ugly but decent icon with Inkscape (I&rsquo;m a developer, not a graphist), and here is <strong><a href="https://market.android.com/details?id=net.cacheux.soundboost" target="_blank">Sound Boost</a></strong>, available on the Android Market. A simple and lightweight application to increase the volume from any audio application on the phone when a wired headset is plugged.</p>
<p>Sadly, managing the sound is very device dependant, so Sound Boost can&rsquo;t work, and probably never will, on every available device. For example, on a Galaxy S2, the sound is decreased. Quite anoying, isn&rsquo;t it ? I&rsquo;ll try to maintain a list of compatible devices to let you know if it worth downloading 20 ko. Any help would be very appreciated !</p>
<ul>
<li>HTC Desire : OK</li>
<li>HTC Desire S : OK</li>
<li>HTC Desire Z : OK</li>
<li>HTC Incredible 2 : OK</li>
<li>HTC Inspire 4G : OK</li>
<li>LG 990 Optimus Speed : OK</li>
<li>Motorola Droid Bionic : OK</li>
<li>Samsung Nexus S : OK</li>
</ul>
<div><span class="Apple-style-span" style="line-height: 18px;"><br />
</span></div>
<ul>
<li>HTC Evo 3D : Very little boost</li>
<li>HTC Legend : Very little boost</li>
<li>HTC Wildfire S : Very little boost</li>
<li>Samsung Galaxy Spica : Very little boost</li>
</ul>
<ul>
<li>Asus EEEPad Transformer : No more sound</li>
<li>HTC Amaze 4G : No difference</li>
<li>HTC Thunderbolt : No difference</li>
<li>LG Optimus One : Sound decreased</li>
<li>Motorola Atrix : No more sound</li>
<li>Motorola Citrus : No difference</li>
<li>Samsung Galaxy S : No difference</li>
<li>Samsung Galaxy S2 : Sound decreased</li>
<li>Samsung Galaxy Tab 7&Prime; : No difference</li>
<li>Samsung Vitality : No difference</li>
<li>Sanio Zio : Sound decreased</li>
</ul>
</div>
<p>
<p><small>This Article is also available in <b>French</b>.</small></p>
]]></content:encoded>
			<wfw:commentRss>http://leo.cacheux.net/blog/2011/10/05/sound-boost-augmentez-le-volume-de-votre-appareil-android/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Un CRUD simple, personnalisable et puissant avec Symfony</title>
		<link>http://leo.cacheux.net/blog/2011/05/27/un-crud-simple-personnalisable-et-puissant-avec-symfony/</link>
		<comments>http://leo.cacheux.net/blog/2011/05/27/un-crud-simple-personnalisable-et-puissant-avec-symfony/#comments</comments>
		<pubDate>Fri, 27 May 2011 20:59:49 +0000</pubDate>
		<dc:creator>Léo</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[crud]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[tutoriel]]></category>

		<guid isPermaLink="false">http://leo.cacheux.net/blog/?p=4</guid>
		<description><![CDATA[Symfony a beau avoir de chouettes scripts pour générer automatiquement des CRUDs, je n&#8217;utilise jamais cette fonctionnalité pour tout un tas de raisons. D&#8217;une part, quand il s&#8217;agit d&#8217;insérer certaines actions, par exemple envoyer un mail à l&#8217;ajout d&#8217;un élément, &#8230;<p class="read-more"><a href="http://leo.cacheux.net/blog/2011/05/27/un-crud-simple-personnalisable-et-puissant-avec-symfony/">Lire la suite &#187;</a></p>]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Symfony a beau avoir de chouettes scripts pour générer  automatiquement des CRUDs, je n&rsquo;utilise jamais cette fonctionnalité pour  tout un tas de raisons. D&rsquo;une part, quand il s&rsquo;agit d&rsquo;insérer certaines  actions, par exemple envoyer un mail à l&rsquo;ajout d&rsquo;un élément, il est  plus efficace d&rsquo;aller ajouter la ligne qui va bien dans son code quand  on a une totale maîtrise de celui-ci. D&rsquo;autre part, les CRUDs générés ne  vont pas toujours coller à la charte graphique voulue. Et quand on nous  fournit une charte complète à intégrer, il vaut mieux la découper pour  l&rsquo;intégrer directement sur une base vierge, plutôt que de retravailler  ce qui a été généré.</p>
<p style="text-align: justify;">MER IL ET FOU ! Il va tout faire à la main ! Que neni que neni,  Symfony permet de faire des choses bien plus intéressantes que d&rsquo;user  plus que nécessaire les touches Ctrl, C et V de son clavier. Et comme on  est malins mais surtout fainéants, on va faire en sorte d&rsquo;écrire un  code qui sera commun à tous nos modules, de façon à ce que l&rsquo;ajout d&rsquo;une  nouvelle table sur le CRUD se fasse en deux lignes. Et une modification  sur son fonctionnement sera faite à un seul endroit et répercutée sur  tous nos modules.<span id="more-4"></span></p>
<p style="text-align: justify;">Notre projet va être extrêmement simple : on a trois tables,  référençant des utilisateurs, des groupes et des machines. Un  utilisateur peut être présent sur plusieurs machines, appartenir à  plusieurs groupes, bref, on a des relations en n-n entre toutes nos  tables. On va donc avoir un modèle de données qui ressemble à ça :</p>
<p><a href="http://leo.cacheux.net/blog/wp-content/uploads/2011/04/model1.png"></a><a href="http://leo.cacheux.net/blog/wp-content/uploads/2011/04/model.png"><img class="size-full wp-image-21 aligncenter" title="Modèle de données CRUD Symfony" src="http://leo.cacheux.net/blog/wp-content/uploads/2011/04/model.png" alt="" width="566" height="334" /></a></p>
<blockquote>
<p style="text-align: justify;"><em>Pourquoi avoir appelé les tables unix_user et unix_group et  pas juste user et group ? Parce que faire un INSERT INTO group sous  MySQL passe plutôt mal, &laquo;&nbsp;group&nbsp;&raquo; étant un mot clé réservé. On pourrait  s&rsquo;en sortir en échappant le nom de la table avec des quotes,  malheureusement, Propel ne le fait pas. On doit donc se passer de la  table group pour tout projet Symfony/Propel, ainsi que d&rsquo;autres mots  clés qu&rsquo;il aurait pu être chouette d&rsquo;utiliser. Attention aussi à éviter  tout ce qui sera utilisé comme classe par le framework (Criteria, des  Base*, etc&#8230;). Et pourquoi unix_user et pas user, alors que ça aurait  quand même pu marcher ? Parce que tant qu&rsquo;à faire, autant uniformiser un  peu.</em></p>
</blockquote>
<p style="text-align: justify;">On cherche à faire un CRUD tout simple avec trois modules, un  pour chaque table à gérer (sauf les tables de relation). A l&rsquo;édition de  chaque élément, des onglets nous permettront de gérer les relations avec  les autres tables. Et parce qu&rsquo;on est aware, on utilisera des onglets  en Ajax pour ça. It&rsquo;s sooooooooo web 2.0.</p>
<p style="text-align: justify;">Comment faire notre base commune à tous les modules ? Vous savez  sans doute que chaque classe *Actions hérite de sfActions. Mais avez  vous déjà pensé à ajouter une classe intermédiaire ? Oui ? Et bien c&rsquo;est  exactement ce qu&rsquo;on va faire ici : on crée une classe myActions (à  placer dans apps/frontend/lib/) qui héritera de sfActions, et on modifie  chaque module pour qu&rsquo;il hérite de myActions plutôt que sfActions. Il  est tout à fait possible de définir nos méthodes execute* dans cette  classe commune, ce qui fait qu&rsquo;aucun de nos modules n&rsquo;aura besoin de  définir d&rsquo;actions (du moins pas pour l&rsquo;instant). Chaque module  référençant une table différente, il faudra juste préciser la table à  utiliser. On fera cela dans la méthode execute, qui est appelée  systématiquement à l&rsquo;exécution d&rsquo;un module, en créant une méthode  initCrud qui prendra en paramètre le nom de la table :</p>
<pre> class myActions extends sfActions
 {
   private $className = null;

   // Initialisation du CRUD avec le nom de la classe du modèle à utiliser
   public function initCrud($className)
   {
     $this-&gt;className = $className;
   }

 ...</pre>
<p style="text-align: justify;">Avec cette seule information, notre classe myActions a tout ce dont  elle a besoin pour fonctionner, et les fichiers actions.class.php  ressembleront juste à ça :</p>
<pre> class groupsActions extends myActions
 {
   public function execute($request)
   {
     $this-&gt;initCrud('UnixGroup');
     // Toujours appeler la méthode du parent à la fin d'execute
     parent::execute($request);
   }
 }</pre>
<p style="text-align: justify;">Notez qu&rsquo;il faut toujours appeler la méthode execute du parent à la fin de la méthode pour que le tout fonctionne correctement.</p>
<p style="text-align: justify;">Il reste maintenant à écrire les méthodes de notre CRUD dans myActions :</p>
<pre> // Fonction d'index pour lister les éléments
 public function executeIndex(sfWebRequest $request)
 {
   $peerClassName = $this-&gt;className . 'Peer';
   // On personnalise le nom de variable contenant la liste des éléments
   $list_var = strtolower($this-&gt;className) . '_list';
   // On récupère la liste des éléments
   $this-&gt;$list_var = $peerClassName::doSelect(new Criteria());
 }</pre>
<p style="text-align: justify;">Pour l&rsquo;index, on a juste besoin de faire un doSelect. Notre variable  className initialisée plus tôt va nous servir à retrouver la bonne  classe Peer à utiliser. On profite également de la capacité de PHP à  pouvoir utiliser des noms de variables dynamiques pour que celle qui  contiendra notre liste d&rsquo;éléments ait un nom qui sera fonction du type  de contenu : la variable sera $unixuser_list, $unixgroup_list ou  $machine_list selon le module. On pourrait aussi garder un nom générique  pour tous, comme $item_list, ce qui nous économiserait même quelques  modifications sur les templates, mais on est des winners et on utilise à  fond les possibilités de PHP. On pourra plus tard ajouter des options  pour le tri, le filtrage, la pagination&#8230; mais on va rester simple  pour le moment.</p>
<pre> // Fonction identique pour les méthodes d'ajout/édition
 public function executeEdit(sfWebRequest $request)
 {
   $peerClassName = $this-&gt;className . 'Peer';
   $formClassName = $this-&gt;className . 'Form';

   // Si on a un id défini, on édite un objet existant, il faut donc le récupérer
   $object = false;
   if ($request-&gt;getParameter('id'))
     $object = call_user_func(array($peerClassName, 'retrieveByPk'), $request-&gt;getParameter('id'));

   if ($object)
     $this-&gt;form = new $formClassName($object);  // Cas d'un objet existant
   else
     $this-&gt;form = new $formClassName();         // Cas d'un nouvel objet
 }</pre>
<p style="text-align: justify;">L&rsquo;action edit sera la même que l&rsquo;on crèe un nouvel objet ou qu&rsquo;on en  édite un ancien, selon qu&rsquo;on a défini le paramètre id ou pas. On  récupère donc le formulaire correspondant à la classe qu&rsquo;on manipule. Je  ne m&rsquo;étendrais pas sur l&rsquo;utilisation du framework de formulaires de  Symfony, car celui-ci est plutôt bien documenté et parce qu&rsquo;on n&rsquo;en fait  pas grand chose de particulier. J&rsquo;y ai juste ajouté quelques  validateurs pour traiter les erreurs, que vous pourrez voir dans les  sources du projet.</p>
<pre> // Fonction pour la création/modification d'un objet
 public function executeUpdate(sfWebRequest $request)
 {
   $peerClassName = $this-&gt;className . 'Peer';
   $formClassName = $this-&gt;className . 'Form';

   $this-&gt;form = new $formClassName();
   $params = $request-&gt;getParameter($this-&gt;form-&gt;getName());
   // Si on a un id défini, on charge l'objet correspondant
   if (isset($params['id']) &amp;&amp; $params['id'])
     $object = call_user_func(array($peerClassName, 'retrieveByPk'), $params['id']);

   // On recrèe le formulaire en chargeant l'objet
   if (isset($object) &amp;&amp; !is_null($object))
     $this-&gt;form = new $formClassName($object);

   $this-&gt;processForm($request, $this-&gt;form);
   $this-&gt;setTemplate('edit');
 }

 // Validation du formulaire pour les ajouts/éditions
 protected function processForm(sfWebRequest $request, sfForm $form)
 {
   $form-&gt;bind($request-&gt;getParameter($form-&gt;getName()), $request-&gt;getFiles($form-&gt;getName()));
   if ($form-&gt;isValid())
   {
     // En cas de succès, on redirige vers la page souhaitée
     $object = $form-&gt;save();
     $this-&gt;redirect($this-&gt;getModuleName() . '/edit?id='.$object-&gt;getId());
   }
 }</pre>
<p style="text-align: justify;">Pour l&rsquo;action effectuant les ajouts et modifications en base, même  principe, tout est regroupé dans une même méthode. C&rsquo;est un peu plus  lourd ici puisque l&rsquo;id étant intégré au tableau destiné au formulaire,  il ne peut pas être récupéré directement et on doit d&rsquo;abord instancier  un premier formulaire vide. Si on a chargé un objet, on recrée le  formulaire pour le prendre en compte. Les habitués de la génération de  CRUD Symfony reconnaîtront la méthode processForm, qui effectue le  traitement du formulaire, ici quelque peu modifiée pour être adaptée à  n&rsquo;importe quel module. Étant parti sur la base d&rsquo;un module de CRUD auto  généré, j&rsquo;en ai conservé certaines parties. J&rsquo;ai tout de même supprimé  la distinction entre les méthodes create et update, afin d&rsquo;alléger le  code.</p>
<pre> // Fonction pour la suppression d'un ou plusieurs objets
 public function executeDelete(sfWebRequest $request)
 {
   $peerClassName = $this-&gt;className . 'Peer';

   foreach ($request-&gt;getParameter('delete') as $id)
   {
     if ($object = call_user_func(array($peerClassName, 'retrieveByPk'), $id))
     {
       try {
         $object-&gt;delete();
       } catch (PropelException $e) {

       }
     }
   }

   $this-&gt;redirect($this-&gt;getModuleName() . '/index');
 }</pre>
<p style="text-align: justify;">La suppression doit pouvoir être faite sur plusieurs objets à la  fois, via des cases à cocher dans les différentes listes. Rien de bien  compliqué ici : on récupère les objets un à un et on en fait la  suppression. On pourrait améliorer les choses en passant par un doDelete  contenant un Criteria avec la liste des ids à supprimer, mais la  flemme. J&rsquo;ai également laissé de côté la gestion des erreurs, dont il  sera question plus tard.</p>
<p style="text-align: justify;">Voilà, en quelques lignes, une base de CRUD fonctionnelle et qui  permet d&rsquo;ajouter facilement de nouvelles tables en une poignée de  minutes. Je ne détaille pas la création des templates associés, pour  lesquels seuls deux sont nécessaires par module (index et edit), l&rsquo;idée  étant que chacun puisse les adapter à sa propre charte grapĥique. Ils  sont toutefois présents dans l&rsquo;archive jointe, sinon, ça ne marcherait  pas (forcément). Si pour l&rsquo;instant il s&rsquo;agit beaucoup de copier/coller,  on verra plus tard comment simplifier leur création en utilisant des  helpers. Vous remarquerez aussi que je ne me suis pas foulé pour faire  quelque chose de joli et de pratique, mais après tout, ce n&rsquo;est pas le  but ici.</p>
<p style="text-align: justify;">Et là vous me dites : où sont nos beaux onglets Ajaxisés pour  gérer les relations ? Parce que oui, pour l&rsquo;instant nos tables  supplémentaires ne servent strictement à rien. Et bien ceci, ainsi que  plein d&rsquo;autres bonnes choses, sont à venir dans la suite du  tutoriel.</p>
<p style="text-align: justify;"><a href="http://leo.cacheux.net/files/symfony_crud_1.zip">Le code source du tutoriel est ici</a></p>
]]></content:encoded>
			<wfw:commentRss>http://leo.cacheux.net/blog/2011/05/27/un-crud-simple-personnalisable-et-puissant-avec-symfony/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
