AIR Mobile – Prendre une photo (CameraUI ou CameraRoll) et garder les headers intacts
Avec Adobe AIR, vous avez la possibilité de récupérer les photos de l'utilisateur, soit en passant par la Galerie avec CameraRoll ou en passant par l'appareil photo "natif" avec CameraUI.
Vous pouvez ensuite récupérer une variable de type MediaPromise qui va vous permettre d'accéder au contenu de la photo. Les systèmes de stockages de chaque plate-forme étant différents, certaines techniques ne fonctionnent pas sur iOS (pas de vrai système de fichiers).
Heureusement, il y a une solution cross-platform qui fonctionne sous iOS, Android et même BlackBerry Tablet OS:
AIR pour Android – Prendre des photos / vidéos avec CameraUI
Jusque là, tout va bien mais essayez donc le code suivant, très simple, qui va prendre la photo et l'écrire sur le stockage du téléphone:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
<fx:Script>
<![CDATA[
private var _cameraRoll:CameraRoll = null;
private var _mediaPromise:MediaPromise = null;
private var _cameraUILoader:Loader = null;
private var _stream:FileStream = null;
private var temp:File = null;
private var dataSource:IDataInput;
private var _uploadDir:String = "D:/GeoWeb/Multimedia/Equipement/888/";
private function raiseGalleryCall():void {
if (!_cameraRoll) {
_cameraRoll = new CameraRoll;
_cameraRoll.addEventListener(MediaEvent.SELECT, onCameraRollSelection);
}
// demande de récupération d'image au système Android
_cameraRoll.browseForImage();
}
private function onCameraRollSelection(event:MediaEvent):void {
loadImage(_mediaPromise);
}
private function onMediaLoaded(event:Event):void {
readMediaData();
}
private function readMediaData():void {
loadImageHandler(null);
}
public function loadImage(mediaPromise:MediaPromise):void {
if (!_cameraUILoader) {
//Loads the MediaPromise object
_cameraUILoader = new Loader();
}
_cameraUILoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadImageHandler);
_cameraUILoader.loadFilePromise(mediaPromise);
}
public function loadImageHandler(event:Event):void {
var ba:ByteArray;
_cameraUILoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadImageHandler);
var li:LoaderInfo = LoaderInfo(event.currentTarget);
ba = li.bytes;
var now:Date = new Date();
var filename:String = "IMG" + now.fullYear + now.month + now.day + now.hours + now.minutes +
now.seconds + ".jpg";
//temp = File.createTempDirectory().resolvePath(filename);
temp = File.documentsDirectory.resolvePath(filename);
if (!_stream) {
_stream = new FileStream();
//_stream.addEventListener(Event.COMPLETE, onTempFileStreamComplete);
_stream.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorStream);
_stream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, onOutputProgress);
_stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
}
_stream.openAsync(temp, FileMode.WRITE);
_stream.writeBytes(ba);
}
protected function onProgress(event:ProgressEvent):void {
//trace(event);
}
protected function onOutputProgress(event:OutputProgressEvent):void {
if (event.bytesPending == 0) {
onTempFileStreamComplete(null);
}
}
protected function onIOErrorStream(event:IOErrorEvent):void {
trace("onIOErrorStream");
}
protected function onTempFileStreamComplete(event:Event):void {
_stream.close();
}
private function onError(event:Event = null):void {
trace("onError");
}
]]>
</fx:Script>
<s:Button label="___________" click="raiseGalleryCall()" />
</s:View>
Ici, on a simplement un bouton qui va appeler la galerie sur le téléphone. On récupère la MediaPromise, que l'on passe dans un Loader puis une fois la MediaPromise chargée, on récupère les bytes et on les écrit dans un fichier sur la carte SD. En gros, on a fait une copie du fichier de la Galerie sur la carte SD.
Très bien, on récupérons ensuite l'image copiée sur la carte SD sur son PC. Windows n'arrive pas à générer d'aperçu et affiche ceci lorsque l'on veut l'ouvrir:
Windows n'arrive pas à lire le fichier qui semble corrompu, pourtant après vérification, on a bien écrit tous les bytes de l'image, celle-ci fait exactement le même poids. C'est en fait en comparant le fichier dans éditeur de texte (Notepad++) que l'on s'aperçoit d'une différence:
A gauche, on a l'image originale, à droite la copie. Les fichiers sont identiques mais les headers diffèrent légèrement. Si vous uploadez la photo directement sur votre serveur, vous aurez le même problème car les headers sont mauvais. Vous pourriez repasser les bytes dans un PNGEncoder (cela fonctionne) mais cela a un gros impact sur les performances en mobile.
La solution MediaPromise::open()
Après pas mal de recherches en vain, j'ai trouvé la solution qui est la manière par laquelle on charge la MediaPromise. Pour une raison que je ne connais pas, la méthode loadFilePromise(…) va vous pourrir vos header. Il faut en fait lire le flux d'une manière différente, en utilisant la méthode open sur MediaPromise.
Attention, la MediaPromise peut être synchrone ou asynchrone, pensez bien à prendre en compte les 2 cas.
Voici la partie du code à remplacer:
private function onCameraRollSelection(event:MediaEvent):void {
private var dataSource:IDataInput;
...
dataSource = null;
_mediaPromise = event.data as MediaPromise;
dataSource = _mediaPromise.open();
if (_mediaPromise.isAsync) {
trace("Asynchronous media promise.");
var eventSource:IEventDispatcher = dataSource as IEventDispatcher;
eventSource.addEventListener(Event.COMPLETE, onMediaLoaded);
//readMediaData();
} else {
trace("Synchronous media promise.");
readMediaData();
}
}
private function onMediaLoaded(event:Event):void {
readMediaData();
}
private function readMediaData():void {
loadImageHandler(null);
}
public function loadImage(mediaPromise:MediaPromise):void {
if (!_cameraUILoader) {
//Loads the MediaPromise object
_cameraUILoader = new Loader();
}
_cameraUILoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadImageHandler);
_cameraUILoader.loadFilePromise(mediaPromise);
}
public function loadImageHandler(event:Event):void {
var ba:ByteArray;
ba = new ByteArray();
dataSource.readBytes(ba);
var now:Date = new Date();
var filename:String = "IMG" + now.fullYear + now.month + now.day + now.hours + now.minutes +
now.seconds + ".jpg";
//temp = File.createTempDirectory().resolvePath(filename);
temp = File.documentsDirectory.resolvePath(filename);
if (!_stream) {
_stream = new FileStream();
//_stream.addEventListener(Event.COMPLETE, onTempFileStreamComplete);
_stream.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorStream);
_stream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, onOutputProgress);
_stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
}
_stream.openAsync(temp, FileMode.WRITE);
_stream.writeBytes(ba);
}
Et miracle, votre image va être strictement identique à l'originale, vous pourrez donc l'uploader tranquille sur votre serveur
[Offre d'emploi] – Développeur ActionScript 3 confirmé sur Paris
Développeur ActionScript 3 confirmé sur Paris – Offre d'emploi
- Titre: Développeur ActionScript 3
- L'entreprise: Sportdub (http://www.sportdub.com) est le premier site de commentaires sportifs amateurs en vidéo et en direct. On est passionnés de sport, bosseurs et on veut tout changer à ton expérience devant un match TV. On est en plein lancement et on cherche quelqu'un au niveau pour rejoindre :
- 1 start-up : 4 personnes, 4 profils complémentaires (Tech / Bizz / Web / Management)
- Avec une grosse ambition : tout changer dans le monde du sport à la TV
- Et avec des bons défis techs sur le streaming vidéo
Profil Recherché
- Profil recherché:
- T'as une formation Bac+2 à bac+5, et une expérience de 2 ans autour des technologies ActionScript 3
- Tu maîtrises Actionscript 3 et si tu connais pas Java, l'apprendre te fais pas peur
- T'aimes le sport, t'es autonome et t'as envie de t'engager dans une aventure de start-up
- Intégré au sein du pôle R&D tu seras chargé de maintenir et de faire évoluer le module de gestion vidéo :
- Reprise et évolution des composants applicatifs existants
- R&D des solutions vidéo basées sur les technologies de streaming live
- Compétences Techniques Requises:
- T'as déjà une expérience dans les domaines suivants :
- Réalisation de site ou de dispositif développé autour des technologies Flash
- Tu maîtrises les outils Adobe Flex 3, Flash Builder 4
- T'as des notions en Java et t'es prêt à monter en compétence
- T'as déjà une expérience dans les domaines suivants :
- Expérience Requise: 2 ans
- Disponibilité: Dès que possible
Conditions d'embauche
- Lieu : Paris
- Rémunération: selon profil
- Contrat: AE puis CDI
Pour postuler
- Contact:
- Ça y est, t'as trouvé le job de tes rêves ? Envoie un email (CV + LM) à jeveuxvivredemapassion@sportdub.com
[Offre d'emploi] – Ingénieur développement sur Marseille
Ingénieur développement sur Marseille – Offre d'emploi
- Titre: Ingénieur développement
- L'entreprise: IHSystems (http://www.ihsystems.fr) est une société de services en ingénierie informatique spécialisée dans la conception et le développement de logiciels de gestion dans le domaine de la santé.
Profil Recherché
- Profil recherché:
- De formation supérieure en informatique (bac +5), vous justifiez d’une expérience professionnelle au moins 2 ans dans le développement orienté objet en PHP et 1 an en Flex.
- Vous aimez le travail en équipe et êtes passionné(e) d’informatique.
- Compétences Techniques Requises:
- Programmation PHP 5 orienté objet.
- Maitrise de zend studio et flash builder.
- Expérience Requise: 2 ans
- Disponibilité: immédiate
Conditions d'embauche
- Lieu : Marseille saint-menet
- Rémunération: 35 à 40k selon profil
- Contrat: CDI
Pour postuler
- Contact:
- support (AT) ihsystems (POINT) fr
Parsley 3 : une sortie attendue
Tutorial Flex écrit par Bertrand Cirot (http://www.viadeo.com/fr/profile/bertrand.cirot)
Publiez vous aussi sur flex-tutorial!
Pour certains d’entre nous, les Frameworks IOC* sont devenus un incontournable. Pour s’imposer dans un milieu concurrentiel et poursuivre une dynamique d’améliorations nécessaires, Parsley nous offre une toute nouvelle version.
Alors que Cairngorm, depuis sa version 3, propose d’avantage une boite à outils applicable aux interfaces, Parsley, sur les recommandations d’Adobe, devient le must have pour la construction de vos applications, alliant simplicité et efficacité.
Sortie le 6 février dernier et présentant une évolution majeure, jetons un œil à cette nouvelle version.
Parmi les différentes interactions que nous propose cette nouvelle organisation, retenons :
- Parsley Core, à savoir le noyau du Framework, la partie indispensable à ajouter à vos projets Flex ou AS3
- Spicelib, les librairies additionnelles – mais très souvent nécessaires – permettant notamment l’utilisation des Commands ou Popup gérées par Parsley ainsi que du système de Logging.
Tâches et Commands unifiées
Parsley 2 proposait un système de tâches particulièrement utile, destiné à répondre aux problématiques suivantes :
- faire exécuter une série d’actions – souvent asynchrones – en parallèle ou en série
- être informé lorsque toutes auront été effectuées
Exemple : Afficher une vue uniquement lorsque toutes les données auront été récupérées, en base de donnée, suite à plusieurs appels serveur simultanés.
Avec la version 3, les tâches disparaissent, mais pas de panique : les Commands ont été améliorées, et offrent les mêmes avantages voire plus encore.
Commands et Tasks étant redondantes, elles ont été unifiées. On peut désormais créer des groupes de Commands en parallèle ou séquentielle, déclenchées par envoi d’évènement ou de manière explicite. Un des apports de cette version 3 réside dans le cas séquentiel, avec la création de règles sur le résultat d’une Command permettant d’engager l’action suivante. On pourra également passer le résultat d’une Command en paramètre d’entrée, ce qui permet d’enchainer les actions. Un potentiel intéressant afin de se simplifier la vie !
Exemple de Commands séquentielles (Extrait de la Documentation) :
On souhaite réaliser successivement les actions suivantes :
- Récupérer un utilisateur en fonction d’un identifiant et d’un mot de passe
- Charger le profil de l’utilisateur ainsi récupéré
On déclare une séquence de Commands déclenchée par un LoginMessage dans le fichier de Context Parsley :
<Objects xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns="http://www.spicefactory.org/parsley">
[...]
<parsley:MapCommand messageType="{LoginMessage}">
<parsley:CommandSequence>
<parsley:Command type="{LoginCommand}"/>
<parsley:Command type="{GetUserProfileCommand}"/>
</parsley:CommandSequence>
</parsley:MapCommand>
</Objects>
La Command – LoginCommand – utilise en entrée le message envoyé pour déclencher la séquence, il contient l’identifiant et le mot de passe. En résultat, la Command récupère un objet représentant un utilisateur.
public class LoginCommand {
[Inject]
public var service:LoginService;
public function execute(message:LoginMessage):AsyncToken
{
return service.getUser(message.identifiant, message.password);
}
public function result(user:UserVo):void
{
// traitement du résultat
}
public function error(fault:Fault):void
{
// traitement de l'erreur
}
}
Dans la Command suivante – GetUserProfileCommand – Il suffit d’ajouter un paramètre correspondant au résultat précédent pour que celui-ci lui soit passé en entrée:
public class GetUserProfileCommand {
[Inject]
public var service:UserService;
public function execute(message:LoginMessage, user:UserVo):AsyncToken
{
return service.getUserProfile(user.id);
}
public function result(userProfile:UserProfileVo):void
{
// traitement du résultat
}
public function error(fault:Fault):void
{
// traitement de l'erreur
}
}
Pour celles et ceux qui souhaiteraient conserver le système de tâches ou simplement migrer de façon progressive, sachez qu’une librairie optionnelle est prévue afin de garder Parsley rétro-compatible.
Conclusion
Ces éléments, ainsi que de nombreux autres avantages – réutilisabilité, testabilité, qui pourront faire l’objet de publications suivantes – contribuent à rendre cette nouvelle version de Parsley stable et pérenne.
Simplicité et efficacité sont définitivement au rendez-vous avec cette dernière version, au service de tous vos développements applicatifs.
Si je devais résumer, Parsley : Deutsche Qualität !
Pour aller plus loin
- http://www.spicefactory.org/parsley/
- http://www.spicefactory.org/parsley/docs/3.0/manual/
- http://blog.kapit.fr/ria/blog/2012/02/09/parsley-fait-peau-neuve/
*Inversion Of Control
Réunion Tontons Flexeurs Mardi 21 Février avec Deepa Subramaniam et Adam Lehman
Pour ceux qui ne suivent pas le compte Twitter de Michael Chaize et qui sont peut-être passés à côté de l'annonce, il reste encore des places pour la rencontre Tontons Flexeurs de ce mardi 21 février, où vous pourrez notamment découvrir les nouveautés de Flex/FB 4.6 mais surtout de discuter de l'avenir de (Apache) Flex avec Michael, Yann Chevalier mais aussi Deepa Subramaniam et Adam Lehman, respectivement ex product manager du SDK et de Flash Builder.
Pour ceux qui ne peuvent pas s'y rendre, Yann Chevalier a indiqué que la réunion serait diffusée en streaming et qu'il donnerait l'URL sur son compte Twitter avant celle-ci.
Vous pouvez aussi indiquer en commentaire de ce post les questions que vous auriez aimé éventuellement poser, et on essaiera d'obtenir les réponses pour vous lors de la réunion.
Enfin, j'espère avoir le temps de poster rapidement sur le site un point sur Flex, Apache, Spoon et Adobe, qui comprendra évidemment un récapitulatif de ce qui se sera dit mardi.
PS: honoré d'être le premier post de la nouvelle ère libre contribution de flex-tutorial et qui vous encourage à profiter vous aussi de cette tribune pour faire vivre la communauté #FlexFr







