18 novembre 2007
Faire un résume sur un download avec AIR
Je ne l'ai pas testé n'en ayant pas vraiment besoin, mais je trouve la possibilité de le faire intéréssante, donc pour ceux qui le souhaite, voici quelques lignes de code qui permettraient de faire un résume sur un download raté.
C'est .de mais écrit en anglais ... faut vraiment que je m'y mette.
I haven't tested this trick, but the idea is great and possibility nice, you can resume download in your AIR application. Follow the precedent link, they 're the code and the explications.
Enjoy
13 novembre 2007
Lire les ID3 d'un fichier
Lire les balises d'un mp3 peux sembler simple, c'est vrai si on accepte de lancer un objet sound.
Mais mon but étant simplement de récupérer les données pour les afficher, je ne trouvais pas très propre de lancer le son en arrière plan avec un volume à zéro le temps de récupérer les infos.
Heureusement Mr Ben Stucki nous a concoté un super tutoriel sur la lecture d'un fichier binaire et la récupération des tags sur un fichier mp3
http://labs.adobe.com/wiki/index.php/AIR:Articles:Working_with_Binary_Data#Reading_binary_data
L'un des principaux problèmes de ce tuto pour les maceux, c'est l'utilisation de browse .... :( un jour peut être.
Enfin pour ça il suffit de déterminer soit même le chemin du fichier que l'on souhaite lire.
Pour ça je donne le path du fichier comme argument de la fonction.
Un autre petit souci c'est que l'exemple considère que les fichiers sont bien tagés ce qui malheureusement n'est pas toujours le cas, j'ai donc ajouté (en rouge )un test try ... catch pour eviter l'erreur 2030.
Et modifié (en vert) la manière de récupérer les infos notamment parce que sur certains mp3 le titre n'est pas dans TIT2 mais TT2, une question de version je crois.
Le code original et les autres fonctions nécéssaires sont disponibles via le lien précédent.
SongTitle, SongAlbum, SongArtiste sont les noms de mes champs textes ou variables à vous de les adapter au votre.
private function loadLocalID3( pathDuFichier:String ):void {
var file:File = new File();
file.nativePath = pathDuFichier;
var bytes:FileStream = new FileStream();
bytes.open(file, FileMode.READ);
// read in sequence
var tagId:String = bytes.readUTFBytes(3);
var majorVersion:Number = bytes.readByte();
var minorVersion:Number = bytes.readByte();
var flags:int = bytes.readByte();
var tagSize:uint = convertSynchsafe(bytes.readUnsignedInt())
trace("id: " + tagId);
if(flags==0) {
//frames
while(bytes.position < tagSize) {
var id:String = bytes.readUTFBytes(4);
var size:uint = bytes.readUnsignedInt();
var flags1:int = bytes.readByte();
var flags2:int = bytes.readByte();
if(id!="") { trace(id); }
if(id.charAt(0)=="T") {
try{
var encodingFlag:int = bytes.readByte();
var text:String = bytes.readUTFBytes(size-1);
if(id == "TIT2"){
SongTitle.text = text;
}
if(id == "TT2"){
SongTitle.text = text;
}
if(id == "TPE1"){
SongArtist.text = text;
}
if(id == "TALB"){
SongAlbum = text;
}
} catch(error:EOFError){
trace (error.message);
}
} else if(id == "APIC") {
var start:int = bytes.position;
var textEncoding:int = bytes.readByte();
var mime:String = readISO(bytes);
var pictureType:int = bytes.readByte();
var description:String = readISO(bytes);
var stop:int = bytes.position;
var imageSize:int = size-(stop-start);
var image:ByteArray = new ByteArray();
bytes.readBytes(image,0,imageSize);
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.loadBytes(image);
} else {
bytes.position += size;
}
}
SongTitle.text = SongTitle.text+' '+SongAlbum; // Ca c'est perso j affiche le titre et l album dans le même champs
}
}
La ou Mr Stucki fait très fort, c'est sur son blog ou il propose de récupérer le contenu des balises ID3 d'un élément distant (ce post à pratiquement deux ans :)), mais il fonctionne avec flex3 beta 2 donc pas de soucis.
Il faut refaire les opérations citées précédemment dans le code car il y a le même type de problèmes sur les fichiers qui n'ont pas de tags ou pas les mêmes.
D'ici quelques temps, j'espère être moins chargé et faire un tutoriel complet sur le sujet, il me faut aussi un vrai blog avec de l'affichage de code et un moteur de recherche parce que là c'est n'importe quoi :)
06 novembre 2007
Leopard AIR ... ça continu
Dans la série p'tit merdouille .....
J'ai une appli AIR qui télécharge des fichiers, basique, jusqu'a présent (avec Tiger) aucun problème, mais maintenant si le fichier sur le serveur à des espaces dans le nom l'urlencodage ( remplacement des espace, etc), ne se fait plus automatiquement et donc j'ai des erreurs 404 ..... arrrgghhh
Je ne trouve rien dans la doc sur l'urlencode dans l'as3....
[Edit]
Si il y a effectivement un truc, dans les fonctions en top level de l'as il y a encodeURI() qui fait très bine l'affaire
// BINARY parce que je telecharge un fichier
private var dataFormat:String = URLLoaderDataFormat.BINARY;
public function loadIt():void{
var loader:URLLoader = new URLLoader();
loader.dataFormat = dataFormat;
configureListeners(loader); // Des listeners de base complete IO etc ...
// urlDown est un attirbut que je recupere lors de l instantiation monobjet.urlDown = mon url
urlPod = encodeURI(urlDown);
var request:URLRequest = new URLRequest(urlDown);
try {
loader.load(request);
} catch (error:Error) {
trace("Document non trouvé");
}
}
05 novembre 2007
DateFormatter et le tri sur les dates
J'ai un datagrid qui liste le contenu d'un répertoire. A ce niveau là pas de soucis, je veux afficher la date dans une colonne, et là je me retrouve avec la date complète en anglais à peu près comme ça "Sat Nov 30 01:20:00 GMT-0800 1974".
Il existe dans le package mx.formatters une classe DataFormatter, génial ....
Donc on teste
Avant de boucler sur le contenu de mon répertoire je crée mon objet DateFormatter
var dateFormatter:DateFormatter = new DateFormatter();
dateFormatter.formatString ="DD/MM/YYYY";
Puis je boucle sur le contenu du répertoire et pour chaque item je crée un objet myfile:File.
// Je récupère la date du fichier ( j'ai eu des problèmes avec Tiger.... )
var dateObjet:Date = myfile.creationDate;
// Et je la mets au format défini par formatString précedemment.
var laDate:String = dateFormatter.format(dateObjet);
Ensuite on en fait ce que l'on veut, moi je reconstruit un objet pour chaque fichier de ma boucle et je place tout ça dans un tableau qui se trouve être le dataprovider de mon datagrid.
Puis j'affiche ma date formatée dans une colonne, et c'est là que se pose le problème, comme c'est devenu une chaine de caractère (DateFormatter retourne un type String), quand je clic sur le header pour trier, j'obtiens n'importe quoi :(
Je remercie PhilFlash pour la solution http://philflash.inway.fr/flex/dgsimple/dgsimple.html
Donc on oubli tout ce que l'on vient de faire :)
On laisse la date dans son format d'origine pour le moment et c'est sur le datagrid que l'on va faire les traitements.
Sur chaque colonne d'un datagrid on à l'évenement labelFunction qui comme son l'indique permet de déterminer une fonction pour le label ( c'est facile l'anglais, non ? :) )
<mx:DataGridColumn headerText="DATE" dataField="date" width="45" labelFunction="showDate" sortCompareFunction="dateCompareFunction"/>
private function showDate(item:Object, column:DataGridColumn):String
{
if (dateFormat == null) {
dateFormat = new mx.formatters.DateFormatter();
dateFormat.formatString = "DD/MM/YY";
}
var field:String = column.dataField;
return dateFormat.format(item[field]);
}
Donc oui ça retourne un type String, mais c'est juste pour l'affichage, les objets de notre dataprovider ont encore un vrai objet Date.
Et pour le tri
<mx:DataGridColumn headerText="DATE" dataField="date" width="45" labelFunction="showDate" sortCompareFunction="dateCompareFunction"/>
On utilise sortCompareFunction de datagridColumn et on lui affecte une fonction de tri
private function dateCompareFunction(obj1:Object, obj2:Object):int {
var result:int = 0;
result = ObjectUtil.dateCompare(obj1.date, obj2.date);
return result;
}
Et là ça fonctionne :), un bel affichage et un tri.
N'oubliez pas de faire un import des classes
import mx.utils.ObjectUtil;
import mx.formatters.DateFormatter;
04 novembre 2007
Leopard AIR ... ça commence mal
[Edit]
Ce problème est aléatoire, ce matin il avait disparu et en faisant une mise à jour de mon appli il est revenu sur toutes les applis AIR :(
Bien bien bien, je m'attendais à des petits problèmes mais là, ça va pas être simple.
Ce n'est pas bien grave, mais c'est comment dire ... génant.
Avec Léopard et les applis AIR à fond transparent quand je les déplace j'ai un petit trait blanc clignotant qui apparait sur le bord haut et droit de l'appli .... Bien sur une image vaut mieux que cette explication foireuse, je vais voir ce que je peux faire :)
23 octobre 2007
Update d'une appli AIR
Un bon tuto pour la mise à jour d'une appli AIR http://blog.everythingflex.com/2007/10/01/air-update-manager/
En substance
Creer une classe UpdateManager, on peux copier/coller celle de Rich Tretola
Dans le fichier principal de l'appli faire l'import de la classe et la création d'une instance de la classe
import le.path.de.la.classe.UpdateManager;
private var um:UpdateManager = new UpdateManager("http://www.tondomaine.com/appli/version.xml");
Le fichier version.xml est à faire à la main, je reviens dessus après
Dans la fonction d'initialisation de l'appli on appelle la fonction loadApplicationFile()
um.loadApplicationFile();
Cette fonction va lire le numero de version inscrit dans le fichier app.xml de l'appli
monappli-app.xml puis faire la comparaison avec celui de l'appli en ligne (fichier version.xml sur le serveur).
J'ai vu des exemples avec /META-INF/AIR/application.xml mais ça n'a pas fonctionné ...
Dans cette fonction, il faut donc définir le path du fichier dans cette ligne :
var request:URLRequest = new URLRequest("app-resource:/monappli-app.xml");
[Edit]
Mea culpa, en fait il faut bien aller chercher /META-INF/AIR/application.xml mais ça ne fonctionne pas pendant le debug, donc pendant le debug aller chercher app-resource:/monappli-app.xml et pour la version compilée app-resource:/META-INF/AIR/application.xml.
Le fichier version.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<currentVersion version=".2"
downloadLocation="http://www.tondomaine.com/appli/monappli.air"
forceUpdate="false"
message="les nouvelles fonctionnalités"/>
Mettre la version de l'appli qui se trouve sur le seveur, indiqué l'url de l'appli pour le téléchargement, si forceUpdate est à true la mise à jour se fait sans demander son avis à l'utilisateur, le message s'affiche dans l'alerte, on peut y mettre ce que l'on veut.
J'ai eu une erreur 2032, je n'avais pas bien noté le path de l'appli à télécharger dans le fichier version.xml
Voilà c'est un résumé rapide, si vous voulez que je le fasse au ralenti, il suffit de demander :).
18 octobre 2007
MetaData video flex
J'avais vu ça il y a un mois environ mais je ne pouvais pas le tester car ça avait été intégré dans un nigthy build de flex, et que si vous faites du AIR vous ne pouvez pas jouer à récuperer ce genre de version ( si ce n est pas exact, je veux être au courant, vu le nombre de bug qui me prenne la tête et dont j'espère la résolution).
Bref, avec la flex 3 B2 on peut maintenant lire les metadata des videos avec le videoDisplay, il suffit pour cela d'ajouter matadatareceived dans la déclaration du composant et de lui associer une fonction.
<mx:VideoDisplay id="videoDisplay"
visible="false"
ready="videoDisplay.visible = true;"
metadataReceived="videoDisplay_metadataReceived(event);" />
private function videoDisplay_metadataReceived(evt:MetadataEvent):void {
var item:String;
var meta:Object = evt.info; // videoDisplay.metadata;
var value:*;
for (item in meta) {
if (ObjectUtil.isSimple(meta[item])) {
if (meta[item] is Array) {
value = "[Array]";
} else {
value = meta[item]
}
trace('name: '+item +' - value: '+value);
}
}
}
Trouvé sur le très bon blog http://blog.flexexamples.com/
10 octobre 2007
Dragmanager conflit de nom
En voulant mettre en place un drag and drop d'un élément d'une liste vers bouton, j'ai rencotré pas mal de petits soucis.
les nativeDragEvent qui ne fonctionnaient pas et des conflits entre les classes mx.managers.DragManager et flash.desktop.DragManager
La solution est de nommer completement DragManager quand on l'appelle ..
Petit exemple qui fonctionne.
<?xml version="1.0"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
applicationComplete="initApp();">
<mx:Script>
<![CDATA[
import mx.events.DragEvent;
import mx.core.DragSource;
import flash.events.NativeDragEvent;
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;
import mx.managers.DragManager;
import flash.desktop.DragActions;
import mx.core.UIComponent;
import mx.collections.ArrayCollection;
private function initApp():void {
firstList.dataProvider = new ArrayCollection([
{label:"First", data:"1"},
{label:"Second", data:"2"},
{label:"Third", data:"3"},
{label:"Fourth", data:"4"}
]);
}
private function acceptIt2(event:DragEvent):void{
trace ('----------------------------------');
if(event.dragSource.hasFormat("items")) {
mx.managers.DragManager.showFeedback(DragActions.COPY);
mx.managers.DragManager.acceptDragDrop(best);
trace(event.type);
}
}
private function performDrop2(e:DragEvent):void{
trace('naigationControlDroite::performdrop');
}
]]>
</mx:Script>
<mx:HBox id="myHB">
<mx:List id="firstList"
dragEnabled="true"
dragMoveEnabled="true"/>
<mx:Button id="best"
dragEnter="acceptIt2(event)"
dragDrop="performDrop2(event)"
top="93" horizontalCenter="15"
label="Best"/>
</mx:HBox>
</mx:WindowedApplication>
Merci à Iteratif (flexx.fr) et Gwen pour le coup de main
06 octobre 2007
Trouver le user loggé sur l'ordi
Connaitre l'utilisateur qui est en train d'utiliser votre application AIR pour afficher son nom fait parti de ces petites options sympa.
Pour ce faire c'est simple
File.userDirectory.name .... tant pis pour ceux qui ont mis n'importe quoi :)
05 octobre 2007
Petite main ou es tu ?
Non non, je ne viens pas de suivre un cours de F.......g Manager qui cherche un stagiaire, je répond plutôt à un demande d'utilisateur.
Mais ou est donc cette petite main chère à ma mère qui lui indique la possibilité de cliquer ici ou là ?
Eh bien il faut croire que chez Adobe, on considère que ce repère est une option, même sur un composant tel qu'un 'button'.
Alors pour corriger le tir et faire plaisir à ceux qui éprouve un grand sentiment de manque devant votre interface :
<mx:Button x="60" useHandCursor="true" buttonMode="true" y="10" click="onCloseClicked(event)" id="closeBtn" toolTip="Close" />
