Archivo de la categoría: R.I.A.

Adobe AIR y VI – Notificaciones instantáneas ó toast style windows

Llegamos, por fin, al último capítulo de esta serie sobre Adobe Air.

En el proyecto en el que estoy trabajando necesito que se lancen notificaciones ante determinados eventos en el servidor. Podría hacerlo abriendo ventanas normales, claro, pero son avisos que no requieren acción del usuario, sólo son mensajes, así que me gustaría que fuesen no intrusivos, que no molesten al usuario con lo que esté haciendo. Son los típicos mensajes que lanzan algunos programas de esos que se quedan como un icono en la barra de tareas (Messenger, McAfee…).

Tras mucho buscar llegué a la primera conclusión. Este tipo de ventanas las llaman “toast style windows” 😐 . El segundo paso era cómo hacerlas. Seguí buscando y encontré varias clases que implementaban lo que yo quería, si alguien lo ha hecho ya… ¿por qué reinventar la rueda?. Probé varios sistemas y el que más me convenció fue éste, directamente desde Adobe. Sencillamente lo controla todo, desde lanzar varias ventanas y que se apilen una sobre otra a lo alto del escritorio hasta controlar si el usuario está utilizando el ordenador o no para no eliminarlas sin que las vea. Sencillamente perfecto. Con algo de estilos pueden quedar muy bien.

Toast windows con AirEl ejemplo del que os hablo venía para hacer con Adobe Flash, no con Flex, pero se adapta sólo.

Lo primero que necesitaremos son las clases que lanzan las ventanas. Os las dejo aquí.

Y ahora las utilizamos en la aplicación.

import com.xplota.display.DisplayManager;
import com.xplota.display.MessageWindow;

private var displayManager:DisplayManager;

function nuevoMensaje(texto:String):void(){
    displayManager = new DisplayManager();
    displayManager.displayMessage(texto);
}

nuevoMensaje("prueba de nueva ventana");

Ya está! Eso es todo. Sencillo ¿eh? 😉 . Prueba a lanzar varias ventanas y verás como se apilan automáticamente unas sobre otras. Si haces click sobre una de ellas verás que desaparece, da igual en cual, deja ese hueco. Si esperas 10 segundos (configurables), verás que la ventana desaparece sóla, pero si no utilizas ni el teclado ni el ratón, el sistema supone que no estás viendo lo que ocurre y mantiene ahí los mensajes para que no los pierdas. No olvides diseñarlas un poco más chulas que las que trae por defecto 😛 .

Hasta aquí hemos llegado con este minicurso de AIR. Creo que he cubierto la mayoría de cosas que se necesitan para comenzar una aplicación decente en AIR, aún así es posible que añada alguno más, quién sabe…

Espero que os hayan sido útiles.

Adobe AIR V – Trabajando con bases de datos locales sqlite

Llegamos al penúltimo capítulo de esta serie acerca de Adobe AIR. Hoy veremos como trabajar en local con datos persistentes de manera que podamos recuperarlos en otras sesiones. Al decir en local me refiero a que no se necesita conexión a Internet para salvaguardarlos, no se utilizará una base de datos remota sino el propio ordenador del cliente.

A diferencia de las aplicaciones Flash embebidas en un navegador, AIR permite el acceso completo al sistema de archivos del sistema operativo, no tiene las limitaciones de la máquina virtual Flash ni las políticas de seguridad, así que podemos crear, abrir, modificar y guardar cualquier archivo.

Si unimos todo esto con sqlite obtenemos lo que buscamos ya que AIR trae soporte nativo para sqlite con lo que guardar y consultar datos con sentencias sql no será un problema, más sencillo imposible.

AIR permite dos modos de trabajo con sqlite, síncrono y asíncrono. La diferencia es obvia, en el primer tipo se espera a que la ejecución de las instrucciones sql termine para continuar la ejecución de la aplicación, mientras que en el modo asíncrono se utilizan eventos que se disparan cuando se da por finalizada la consulta. En modo sincrono debemos ejecutar todo enjaulado en estructuras try/catch para prevenir errores y excepciones.

Aunque puedes crear la base de datos directamente desde la propia aplicación con sentencias CREATE TABLE, yo prefiero distribuir la aplicación con la base de datos ya creada, es mucho más cómodo. Para crearlas suelo usar dos utilidades:

También puedes hacer tu propia aplicación AIR para gestionar bases de datos sqlite, no conozco ninguna pero sería muy útil. ¿Quién se atreve? 🙂

Con cualquiera de ellas podremos crear la estructura de nuestra base de datos que copiaremos en el directorio del código fuente, para que al compilarla y empaquetarla se distribuya con ella.

Como no todos los sistemas operativos permiten a cualquier usuario escribir en la ruta de instalación de las aplicaciones, lo mejor y más recomendable es copiar la base de datos “base” en la carpeta de usuario del cliente que está ejecutándola ya que ahí siempre tiene permisos. Para hacerlo AIR nos provee de todos los mecanismos necesarios.

Con los métodos userDirectory y applicationDirectory de la clase File accedemos a las rutas de usuario y de la aplicación respectivamente independientemente del sistema operativo sobre el que está corriendo la aplicación, con lo cual hacemos el proceso completamente transparente.

var a:File=File.userDirectory.resolvePath("basedatos.db");
if(!a.exists){
    var b:File=File.applicationStorageDirectory.resolvePath("basededatos.db");
    b.copyTo(a, true);
}

Sencillo y evidente, sobran más explicaciones. Sabiendo esto es ya muy fácil conectar a la base de datos. Utilicemos el método síncrono antes comentado.

private var dbFile:File;
private var conn:SQLConnection;
private var q:SQLStatement;  

private function iniciaDB():void {
    var dbFile:File=File.userDirectory.resolvePath("basededatos.db");
    if(!dbFile.exists){
        var b:File=File.applicationStorageDirectory.resolvePath("basededatos.db");
        b.copyTo(dbFile, true);
    }
    conn = new SQLConnection();
    try{
        conn.open(dbFile);
    }catch (error:SQLError){
        Alert.show("ERROR ABRIENDO BBDD "+error.message+" "+error.details);
    }
}

¡Ya estamos dentro de la base de datos! Sólo nos queda ejecutar consultas y recuperar los resultados. No tiene mucha ciencia. Veamos un ejemplo para que queda bien claro.

var q:SQLStatement;
var result:SQLResult;

q.text = "SELECT campo1, campo2 FROM tabla";

try{
   q.execute();
   result=q.getResult();
   if(result.data){
     var numResults:int = result.data.length;
     for (var i:int = 0; i < numResults; i++){
        var r:Object = result.data[i];
        //accedemos a los campos desde el objeto, r.campo1, r.campo2
     }
   }
}catch (error:SQLError){
  //la consulta nos ha devuelto un error
}

Eso es todo como iniciación sqlite desde AIR. Del mismo modo se pueden ejecutar instrucciones INSERT, DELETE

A partir de aquí puede seguir cualquiera 😛 .

En el próximo y último capítulo veremos como hacer ventanas de notificación instantáneas y no intrusivas, del estilo de los avisos de conexión del Messenger 😉 .

Adobe AIR IV – Detectando el estado de la conexión

Continuamos con la serie de artíulos sobre Adobe Air. Hoy trataremos un tema curioso como es detectar si el cliente dispone de conexión a Internet. ¿Qué utilidad puede tener saberlo? Sencillo, permitir que la aplicación trabaje en modos offline y online.

Esta parte viene bastante bien explicada en el manual. Existe un evento “NETWORK_CHANGE” que no sirve para nuestro propósito 😐 .  ¿Para qué sirve entonces? Para detectar cambios en las interfaces de red en general. Este evento se dispara cada vez que se producen modificaciones en alguna de las interfaces de red de la máquina que ejecuta la aplicación, lo que no quiere decir que cambie el estado de la conexión a Internet ya que este evento no sabe si hay o no salida a Internet ni a través de cual de las interfaces se sale. ¿Qué utilidad puede tener entonces? No lo sé 😛 , pero ahí está el evento por si lo quieres utilizar para algo…

NativeApplication.nativeApplication.addEventListener(Event.NETWORK_CHANGE, onNetworkChange);
function onNetworkChange(event:Event)
{
    //Check resource availability
}

Para nuestro propósito debemos utilizar la clase URLMonitor que, como su propio nombre indica, sirve para monitorizar una URL. En efecto, así es como AIR detecta el estado de la conexión, conectando periódicamente a la URL que le indiquemos. Si no puede alcanzarla asume que te has quedado sin salida a Internet. Vamos, que si estábais esperando algo superavanzado y chulo, podéis seguir esperando, es el método más tradicional que existe: leches, no puedo conectar con Google, habré perdido la wifi. Es lo mismo 😛 .

import air.net.URLMonitor;
import flash.net.URLRequest;
import flash.events.StatusEvent;

var monitor:URLMonitor;
monitor = new URLMonitor(new URLRequest('http://www.adobe.com'));
monitor.addEventListener(StatusEvent.STATUS, announceStatus);
monitor.start();

function announceStatus(e:StatusEvent):void {
    trace("Status change. Current status: " + monitor.available);
}

Dentro de la función, lo que nos interesa es el valor de monitor.available, true ó false, lo que nos indicará si podemos o no conectar con la URL. Éste es, en realidad, el concepto de URLMonitor, determinar si una URL es alcanzable o no. Que una URL no sea alcanzable no quiere decir que no tengas acceso a Internet, sólo que no tienes con aquella URL. La comprobación, por tanto, debería hacerse con la URL del backend de nuestra aplicación y no con cualquier URL en general ya que de quien depende nuestra aplicación es de nuestro backend, no de Google 😛 .

El mismo sistema se puede utilizar para comprobar el estado de cualquier aplicación que escuche en un puerto distinto al 80. La explicación sería exactamente la misma y lo haríamos de este modo:

import air.net.ServiceMonitor;
import flash.events.StatusEvent;

socketMonitor = new SocketMonitor('www.adobe.com',6667);
socketMonitor.addEventListener(StatusEvent.STATUS, socketStatusChange);
socketMonitor.start();

function announceStatus(e:StatusEvent):void {
    trace("Status change. Current status: " + socketMonitor.available);
}

En el próximo capítulo veremos cómo añadir potencia a nuestras aplicaciones utilizando sqllite para guardar datos offline. Si combinamos la posibilidad de guardar datos en la aplicación con la de comprobar el estado de red contra el backend podremos hacer que nuestra aplicación funcione en modo offline guardando lo que necesitase enviar y procesándolo cuando tenga conexión. Creo que no se ha entendido bien, pongamos un ejemplo práctico.

Tengo una aplicación que permite enviar SMS a móviles a través de una pasarela HTTP que será quien haga los envíos. Mi aplicación pide el número de destino y el texto a enviar, conecta con la pasarela y le pasa el mensaje que quiero enviar. Como es lógico, si en el momento de enviar no tengo conexión con la pasarela, no podré enviar el SMS. Tengamos ahora presente lo que acabamos de ver en este artículo… ¡puedo saber en cada momento si la pasarela HTTP es alcanzable!  Si cuando voy a hacer el envío no tengo conexión, los guardo en mi base de datos sqllite y cuando cambie el estado de la conexión (URLMonitor me avisará) lanzo todos los envíos pendientes.

Seguro que si lo piensas un poco tú también le encuentras alguna utilidad 🙂 . Lo dicho, en el próximo capítulo… sqllite.

Adobe AIR III – Actualizando automáticamente las aplicaciones

He aquí otra de esas cosas superchulas que vemos en aplicaciones de otros. Ejecutas una aplicación y automáticamente te dice que hay una nueva versión y si quieres actualizarla. Si aceptas, se actualiza ella sola.

Esto en AIR es extremadamente sencillo, sólo necesitaremos unas pocas líneas de código 🙂 .

La primera parte consiste en invocar la comprobación desde tu aplicación, en el evento creationComplete.

private function actualiza():void{
appUpdater.updateURL = "http://tuservidor.com/update.xml";
appUpdater.isCheckForUpdateVisible = false;
appUpdater.addEventListener(UpdateEvent.INITIALIZED, onUpdate);
appUpdater.addEventListener(ErrorEvent.ERROR, onError);
appUpdater.initialize();
}

private function onUpdate(event:UpdateEvent):void {
	appUpdater.checkNow(); // Go check for an update now
}
private function onError(event:ErrorEvent):void {
	Alert.show(event.toString());
}

La segunda es crear un xml en la ruta indicada en “updateURL” con la información de la última versión disponible y la url a la misma. Muy sencillo.

<?xml version="1.0" encoding="utf-8"?>
<update xmlns="http://ns.adobe.com/air/framework/update/description/1.0">
<version>v0.2</version>
<url>http://tuservidor.com/tuaplicacion.air</url>
<description><![CDATA[
v0,2
Aquí puedes incluir información sobre la nueva versión...
Nuevas funciones, opciones...
]]></description>
</update>

Sube el XML al servidor junto a la nueva versión de la aplicación. Recuerda que al compilarla deberás decirle que la versión ha cambiado ya que este es el parámetro que se utiliza para la comprobación, el número de versión.

Eso es todo, tu aplicación se actualizará automáticamente si hay nuevas versiones.

En el próximo capítulo veremos cómo detectar el estado de la conexión a Internet y su utilidad.

Adobe Air II – Aplicaciones en la barra de tareas

Después de ver cómo crear los distintos tipos de ventana con los que se puede crear una aplicación AIR, veremos hoy como hacer una de esas cosas tan chulas que se minimizan en un icono al lado del reloj de Windows.

Es muy sencillo una vez, de nuevo, se entiende el concepto. No todos los sistemas operativos permiten lo que queremos hacer, Windows, Mac OSX y algunos Linux. Para saber si lo soporta AIR dispone de dos métodos, supportsSystemTrayIcon, para entornos tipo Windows, y supportsDockIcon, para entornos tipo Mac.

Antes de meternos en berenjenales necesitaremos, al menos, dos iconos para nuestra aplicación (gif o png si es transparente) a 16×16 píxeles y a 128×128.

En este código veremos cómo asignar un icono en la barra de tareas o en el Dock y crear un menú contextual en este icono con la opción “Salir”, puedes añadir tantas opciones como estimes oportuno.

private function appTray():void{
    var icon:Loader = new Loader();
    var iconMenu:NativeMenu = new NativeMenu();
    var exitMenu:NativeMenuItem = iconMenu.addItem(new NativeMenuItem("Salir"));
    exitMenu.addEventListener(Event.SELECT, function(event:Event):void {
        NativeApplication.nativeApplication.icon.bitmaps = [];
        NativeApplication.nativeApplication.exit();
    });

    if (NativeApplication.supportsSystemTrayIcon) {
        icon.contentLoaderInfo.addEventListener(Event.COMPLETE, iconLoadComplete);
        icon.load(new URLRequest("icons/icono16.png"));

        var systray:SystemTrayIcon = NativeApplication.nativeApplication.icon as SystemTrayIcon;
        systray.tooltip = "Mi Aplicación";
        systray.menu = iconMenu;
        systray.addEventListener(ScreenMouseEvent.CLICK, restauraVentana);
    }

    if (NativeApplication.supportsDockIcon){
        icon.contentLoaderInfo.addEventListener(Event.COMPLETE,iconLoadComplete);
        icon.load(new URLRequest("icons/icono128.png"));
        var dock:DockIcon = NativeApplication.nativeApplication.icon as DockIcon;
        dock.menu = iconMenu;
    }
}

private function restauraVentana(e:ScreenMouseEvent):void{
	stage.nativeWindow.visible=true;
	stage.nativeWindow.orderToFront();
}

Como veis, creamos el menú que le vamos a asignar y cargamos el icono adecuado dependiendo del soporte del sistema operativo. Eso es todo.

Simplemente llamaríamos a esta función en el evento creationComplete de la aplicación y ya la tendríamos integrada en la barra de tareas o en el Dock.

Éste es el resultado.

Aplicacion AIR en la barra de tareas

Sencillo ¿no?. En el siguiente capítulo, aplicaciones que se actualizan automáticamente si hay una versión nueva.

Adobe Air I – Tipos de ventanas y cómo hacer aplicaciones con nuestro propio chrome

Aprovechando que los últimos días me he metido de lleno a desarrollar una pequeña aplicación con Adobe Air, comienzo una serie de artículos dónde iré contando algunos de los entresijos de este sistema ya que parece muy sencillo pero cuando quieres hacer cosas concretas parece que se complica un poco.

Hoy veremos la parte fundamental de una aplicación, las ventanas en sí mismas. Se conoce como “chrome” al entorno sobre el que se crea la ventana. Habitualmente es el propio sistema operativo el que lo hace, son las ventanas clásicas de la mayoría de aplicaciones. En AIR se crean como “WindowedApplication” en la propia aplicación (veremos más adelante que éste es un detalle importante) y en el xml descriptor de la aplicación se configuran estos parámetros:

<initialwindow>
    <systemChrome>standard</systemChrome>
    <transparent>false</transparent>
</initialWindow>

El resultado, bajo Windows, sería la clásica ventana con la estética del sistema operativo.

Ventana Standard en Adobe Air

Vale, pero yo quiero crear mi ventana sin el chrome del sistema operativo.

La primera opción es configurar así los parámetros:

<initialwindow>
    <systemChrome>none</systemChrome>
    <transparent>true</transparent>
</initialWindow>

Y obtenemos una ventana con un chrome AIR.

Venatana none en Adobe Air

Queda muy bonita, pero yo lo que quiero es que no tenga nada de chrome, ni barra superior ni barra inferior, quiero definir yo toda la estética y funcionalidad de mi aplicación. Bienvenidos a la madre del cordero. Esto, que debería estar bien explicado en la documentación, no hay manera de entenderlo, de hecho creo que no se llega a explicar la manera concreta de conseguirlo.

La clave del problema es que, en este caso, la aplicación en el MXML no se definde como  “WindowedApplication” sino como “Application” estándar, el de cualquier aplicación Flex, y en el descriptor definimos:

<initialwindow>
    <systemChrome>none</systemChrome>
    <transparent>true</transparent>
    <visible>true</visible>
</initialWindow>

Y conseguimos el resultado esperado, sólo nuestra ventana sin adornos.

Ventana con chromless personal en Adobe Air

Es importante remarcar que en este caso hay que ponerle el parámetro visible=true dentro del xml ya que si no lo hacemos no se verá nada hasta que se ponga a true, perdí varias horas hasta averiguar porqué no veía nada.

Esto es todo por hoy. En el siguiente capítulo veremos cómo crear aplicaciones que se quedan como un icono al lado del reloj en Windows o en el Dock de Mac OSX.

Fotomatón, o haciendo fotos instantáneas online

La idea es sencilla, conectas con la webcam de tus usuarios, les haces una foto y la envías a una galería donde se pueden ver los caretos de tus visitantes. La implementación es casi tan sencilla como la teoría y el resultado es increíble y original.

El sistema es el mismo que utilizaba BloggerSnap, un servicio que tuvo un increíble éxito en su lanzamiento y que parece cerrado desde abril. Hace un año estaba por encima del puesto 40.000 en Alexa, seguramente estuvo incluso más alto. Imagino que sería muy complicado mantener el coste del ancho de banda con tanta popularidad. Hace un par de años estuvimos a punto de lanzar un servicio similar y lo desestimamos ya que no veíamos la manera de monetizarlo mientras que, a poco que tuviese éxito, los gastos se dispararían.

En un escenario normal dependerías de software que el usuario tendría que instalar para que se hiciese la foto y posteriormente te la enviase, con lo cual no conseguirías el objetivo de la instantaneidad, pero existe una solución que permite hacerlo todo mediante una aplicación online: Flash, en nuestro caso Flex 😛 .

La potencia de la plataforma Flash en el tratamiento de audio y vídeo va mucho más allá de lo que pueda parecer y permite crear aplicaciones multimedia avanzadas con participación del usuario de manera bastante rápida. Si además dispones de dinero suficiente, puedes combinarlo con Flash Media Server y los límites los pondrá tu imaginación. Otro día crearemos un videochat 😉 .

La clave es el acceso a la cámara y al micrófono del equipo que permite la plataforma Flash previa autorización del usuario, obviamente. Si lo autorizas, el player conectará con tu webcam y comenzará a mostrar el vídeo de la misma y, si la aplicación lo requiere, enviarlo a otros usuarios. Lo mismo se puede hacer con el audio.

En el taller de hoy haremos una pequeña aplicación para crear una galería de fotos instantáneas en web/blog. Los usuarios que lo deseen pueden hacerse la foto con su propia webcam y ésta quedará alojada en la galería. Simple pero llamativo.

La aplicación constará de dos partes:

  • Zona de capturas: una aplicación desarrollada en Flex que permitirá a los usuarios hacerse fotos sin más necesidad que tener una webcam.
  • Galería: una sencilla página html donde se listan todas las fotos que se han hecho los usuarios.

Capturando imágenes

Aunque pueda parecer algo muy complicado veremos a continuación que la facilidad de conectar con la webcam en Flex es impresionante.

Nuestra aplicación consta de dos únicos componentes, una imagen y un botón. La imagen nos aportará simplemente algo de estilo y el botón lo utilizaremos de disparador de nuestra cámara de fotos online.

Hay tres tareas bien diferenciadas:

  • Conectar con la webcam.
  • Capturar la foto.
  • Enviarla a nuestro servidor.

Conectar con la webcam

Con este sencillo código estaremos visualizando en nuestra aplicación la webcam que el usuario tenga conectada a su equipo.

private function insertWebcamVideo():void{
	var videoHolder:UIComponent = new UIComponent();
	if(Camera.names.length>0){
	   camera = Camera.getCamera();
	   if(camera==null || camera.width==-1 || camera.height==-1 || camera.fps==-1){
	   		Alert.show("Lo sentimos, no tienes cámara", "Error", 4, null, null, iconoAlerta);
	   		captura.enabled=false;
	   		camera=null;
	   }else{
		   camera.addEventListener(StatusEvent.STATUS, cerrar);
		   video = new Video(camera.width, camera.height);
		   video.attachCamera(camera);
		   videoHolder.addChild(video);
		   videoHolder.width=video.width;
		   videoHolder.height=video.height;
		   videoHolder.visible=true;
		   videoHolder.y=0;
		   caja.addChildAt(videoHolder, 0);
		   caja.removeChild(fotillo);
	   }
	}else{
   		Alert.show("Lo sentimos, no tienes cámara", "Error", 4, null, null, iconoAlerta);
   		captura.enabled=false;
	}
	//Security.showSettings(SecurityPanel.CAMERA);
}

Tan fácil como comprobar primero si tiene webcam (sino, obviamente, no puede hacerse fotos 😛 ) y posteriormente añadirla a un componente de vídeo que habremos incorporado a un UIComponent para mostrarlo en la aplicación. En este momento ya tenemos la cámara del usuario en vivo en la aplicación.

Capturar la foto

Teniendo el flujo de vídeo en la aplicación, queremos que cuando el usuario presione el botón “Capturar” se le haga la foto.

private function getSnapshot():void{
	var snapshot:BitmapData = new BitmapData(video.width, video.height, true);
	var m : Matrix = new Matrix();
	snapshot.draw(video, m);
	var jpegEnc:JPGEncoder=new JPGEncoder(75);
	var jpegDat:ByteArray = jpegEnc.encode(snapshot);
	var img_src:String = base64Encode(jpegDat);

	if(img_src.length>1600){
		//imagen correctamente capturada
	}else{
		Alert.show("La imagen capturada parece estar vacía", "Error", 4, null, null, iconoAlerta);
	}
}

Con este otro código conseguimos este efecto. Se obtiene una captura bit a bit del objeto de vídeo y se comprime en JPEG para enviarla al servidor. ¡Ya tenemos la foto hecha!

Enviar la foto

Utilizaremos un HTTPService para enviar por POST la foto que hemos realizado y que guardaremos en una base de datos. Añadimos a la función anterior el código para enviar la foto.

private function getSnapshot():void{
	var snapshot:BitmapData = new BitmapData(video.width, video.height, true);
	var m : Matrix = new Matrix();
	snapshot.draw(video, m);
	var jpegEnc:JPGEncoder=new JPGEncoder(75);
	var jpegDat:ByteArray = jpegEnc.encode(snapshot);
	var img_src:String = base64Encode(jpegDat);

	if(img_src.length>1600){
		var temp:Object=new Object();
		temp.imagen=img_src;
		httpService.send(temp);
		cargandoWindow = ventanaEspera(PopUpManager.createPopUp(this as DisplayObject, ventanaEspera, true));
	}else{
		Alert.show("La imagen capturada parece estar vacía", "Error", 4, null, null, iconoAlerta);
	}
}

<mx:httpservice id="httpService" showbusycursor="false" useproxy="false" method="POST" resultformat="text" url="upload.php" result="onResult()"
fault="onHTTPFault(event)" />

Ya está. El script upload.php de tu servidor recibirá por POST la variable “imagen” con el contenido de tu foto. Sólo habrá que guardarla en un archivo y habrás terminado la parta más ¿complicada? del proyecto.

if($_POST['imagen'] & strlen($_POST['imagen'])!=0 & strlen($_POST['imagen'])>1600){
        $bmd=base64_decode($_POST['imagen']);
        $im = fopen("fotos/foto.jpg",'w');
        fwrite($im,$bmd);
        fclose($im);
}

Antes de guardar la imagen en disco podrías añadir un registro a una base de datos que te permita generar después la galería. Esa parte queda a tu elección.

Generando la galería

Esta es la parte fácil, simplemente creas un script PHP (o en el lenguaje que prefieras) que liste las fotos que has ido guardando en la base de datos paginando como creas oportuno. Creo que sobran más comentarios para algo tan fácil.

Conclusiones

Hoy hemos visto cómo de una manera extremadamente rápida y sencilla se puede conectar con la webcam (y el micrófono) de los usuarios. Sólo hemos visto como hacer una fotografía instantánea de lo que se está viendo a través de la cámara, pero el sistema abre un inmenso mundo de posibilidades.

Descárgate aquí el código del proyecto.

Primeros pasos con Flash Media Server (2), autentificando usuarios del chat

Hace unos días veíamos como crear un sencillo chat con FMS.

Hoy, a petición popular, vamos a añadir la primera mejora a nuestra aplicación. Por defecto habíamos diseñado un chat público, cualquiera podía conectarse. Sin embargo, puede que deseemos que sólo nuestros usuarios registrados puedan utilizarlo. Necesitamos entonces que nuestros clientes se puedan autentificar contra nuestra base de datos pero no podemos dejar esa responsabilidad en manos de la aplicación cliente, cualquiera podría hacer otra que se conecte a nuestro servidor sin requerir autentificación.

FMS no puede conectarse con ningún tipo de bases de datos, no es su función, aunque cada vez hay más gente solicitando esta funcionalidad y se espera que en la próxima versión (FMS4) haya alguna novedad al respecto. Sea como fuera, de momento no hay métodos nativos para acceder a bases de datos así que sólo se puede recurrir a lo que un día fue Flash Remoting y hoy son los servicios RPC bajo AMF, el método estándar de acceso a datos desde aplicaciones Flash (con el permiso de los webservices SOAP). Con el tiempo fueron apareciendo versiones en los distintos lenguajes para utilizar el protocolo AMF fuera del amparo de Macromedia (ahora Adobe), pero sin duda uno de los pioneros fue amfphp, el que nos permite comunicar aplicaciones Flash con el servidor a través de PHP.

AMF es un formato para serialización de datos e invocación de métodos remotos y es independiente al lenguaje con el que trabaja el servidor, simplemente debe cumplir las especificaciones. FMS, como no podía ser de otra forma, puede comunicarse con nuestro servidor gracias a este protocolo AMF y esto es precisamente lo que vamos a utilizar para autentificar a nuestros usuarios:

dibujo1.jpg

El proceso es mucho más sencillo de lo que parece.

Comenzaremos por descargar amfphp desde la web oficial.  Lo descomprimes y subes la carpeta “amfphp” a tu servidor en la ruta que desees. Ya tienes lo que es el cerebro del sistema listo, para comprobarlo prueba la URL:

http://tuservidor/amfphp/gateway.php

Si todo va bien, prueba ahora el navegador de servicios, herramienta imprescindible para probar tus métodos remotos:

http://tuservidor/amfphp/browser/

Si no has visto ningún error es que todo va bien. Veamos ahora como crear tu servicio de autentificación.

Dentro de la carpeta amfphp que has subido a tu servidor encontrarás la carpeta services. Crea dentro de ella un nuevo directorio con el nombre de tu aplicación, por ejemplo chat. Creamos ahora dentro de esa carpeta las clases y métodos que se necesiten para crear el servicio de autentificación. Por ejemplo, en mi caso creo Chat.php:

<?
class Chat{
    var $conn;    function Chat(){
      $this->conn=mysql_connect("host", "usuario", "clave");
      mysql_select_db('db', $this->conn);
    }
function login($nick, $pass){
        $rs=mysql_query("select * from usuarios where nick='$nick' AND pass='$pass'");
        if(mysql_num_rows($rs)>0)
          return 1;
        else
          return 0;
    }
}
?>

Sencillo ¿no?. Nuestra clase tiene un método login que nos dice si la pareja nick/clave existe en nuestra base de datos. Obviamente esto es sólo un ejemplo, en tu aplicación haz la clase con todos los métodos y la lógica que necesites.

Si ahora vas de nuevo al Service Browser verás que te aparece la nueva aplicación chat con la clase que has creado. Si accedes a ella puedes probar el método login pasándole los parámetros necesarios. Más sencillo imposible.

Ya tenemos la lógica de autentificación lista. ¿Cómo le decimos ahora a FMS que la utilice? Modificamos el archivo main.asc que habíamos hecho en el capítulo anterior para que antes de aceptar la conexión de un usuario la autentifique.

application.onConnect = function(client, nick, clave, sala){
    var valido=1;
    for( i in this.userList){
        if(String(this.userList[i].nick).toLowerCase()==String(nick).toLowerCase()){
            valido=0;
            break;
        }
    }
    if(valido==1){
        var myServer = NetServices.createGatewayConnection("http://tudominio/amfphp/gateway.php");
        var servicio = myServer.getService("chat.Chat", this);
        var callserver=servicio.login(email, clave);

        this.login_Result=function(result){
            //trace("RESULTADO LOGIN"+result);
            if(result>0){
                client.uid = this.uid;
                client.nick = nick;
                client.sala = sala;
                this.acceptConnection(client);
                this.userList[client.uid] = client;
                this.users_so.setProperty(client.uid, client);
                var msg = "*** Entra " + client.nick + " ***";
                this.chat_so.send("onMsgSend", msg);
                client.call("onUserid", null, client.uid, client.nick);
                this.uid++;
            }else{
                var err = new Object();
                err.message = "Email o contraseña incorrectos";
                application.rejectConnection(client, err);
            }
        }
    }else{
        var err = new Object();
        err.message = "Ya hay un usuario con ese nick";
        trace(err.message);
        application.rejectConnection(client, err);
    }
}

Es tan simple como añadir la comprobación del usuario y clave de nuestro cliente. Indicamos la url de nuestro gateway (amfphp) y llamamos al método login con los datos correspondientes. Lo más importante es la forma en que obtenemos los datos que devuelve el metodo. La llamada es asíncrona, no se paraliza la actividad esperando por un resultado sino que se continua la actividad esperando a recibir la respuesta. Para eso se supone que debe haber definida una función nombremetodoremoto_Result, en nuestro caso login_Result ya que login es el método remoto al que llamamos. Cuando se llame a esta función sabremos si el usuario existe en nuestra base de datos, con lo que aceptamos su conexión al chat, o no existe y la rechazamos.

Como habéis visto, es muy sencillo llamar a métodos remotos desde FMS de manera que aumenten las posibilidades de tu aplicación ya que puedes hacer uso de bases de datos y de sistemas avanzados y siempre de manera muy simple.

Yo te he dado las pistas de cómo hacerlo, el resto queda a tu imaginación.

Generador de códigos de barras en AS3

Hoy vamos a ver un ejemplo sobre cómo generar códigos de barras al vuelo utilizando ActionScript3. Un código de barras es la representación mediante líneas paralelas de distinto grosor de un código numérico o alfanumérico. Esta representación lineal los hace de fácil interpretación para un lector láser. No creo que se necesiten más explicaciones, todo el mundo sabe lo que es un código de barras.

Los códigos de barras lineales pueden ser de distintos tipos, en el ejemplo de hoy generaremos EAN y C128. EAN es el más utilizado a nivel mundial y el utilizado en España, así que será el que nos ocupe hoy. Mi trabajo está basado en la clase para generar códigos C128 de Friedrich Dimmel (lo siento, no localizo el link original). La clase que genera el código EAN no es ni más ni menos que la reescritura en AS3 de la que trae phpBarcode  y adaptada para generar la secuencia de líneas que se dibujan bajo la lógica de Friedrich Dimmel en vez de generar una imagen con GD como hace la clase original. Como casi siempre, este pequeño desarrollo lo hice hace un par de años para un sistema de control de stocks, no me critiquéis mucho el código :P.
swfobject.embedSWF(“/wp-content/uploads/2008/10/codigosdebarras1.swf”, “codigosdebarras”, “470”, “350”, “9.0.000”);

Flash del generador

Un código EAN son 13 dígitos, 12 de información (código de país, empresa y producto) y un dígito de control que ayuda a verificar que la decodificación posterior es correcta.
Para crear la imagen de nuestro código simplemente debemos llamar a la clase generadora  con los parámetros:

  • Código: la cadena que queremos convertir a código de barras.
  • Alto de la imagen generada.
  • Tipo de código (EAN o C128).
  • Mostrar el texto del código (opcional).
  • Color de fondo (opcional).

Por ejemplo:

BarcodeGenerator.generateBarcode("3456789012345", 50, "EAN");

Como hemos visto, el último dígito es de control, con lo que con introducir los 12 dígitos con valor será suficiente, la clase generará el treceavo automáticamente. No voy a explicar detalladamente como generar los códigos EAN, hay suficiente literatura acerca del algoritmo googleando un poco y para ver el resultado lo mejor es que examinéis el código fuente que os dejo más abajo. Creo que lo más curioso e importante es ver cómo generamos la imagen una vez tenemos generado el código de líneas.

public static function generateBarcode(code:String="0", height:Number=60, tipo:String="EAN", showText:Boolean=true, backgroundColor:uint=0xffffff):Canvas {
    var can:Canvas = new Canvas();
    can.horizontalScrollPolicy = "off";
    can.verticalScrollPolicy = "off";
    can.height = height;
    can.setStyle("backgroundColor", backgroundColor);
    var sBarcode:Shape;
    var rect:Rectangle;
    var bd:BitmapData;
    var img:Image;
    var bitmap:Bitmap;
    if(tipo=="EAN"){
      var barcode:BarcodeEAN = new BarcodeEAN();
      barcode.code = code;
      barcode.barHeight = height;
      if(showText) {
        barcode.barHeight -= 15;
      }
      var k:Object=barcode.barcode_encode_ean(code);
      if(!k.bars) return new Canvas();
      var bars:String=k.bars;
      var text:String=k.text;
      sBarcode = barcode.generateBarcode();
      rect = new Rectangle(0, 0, (sBarcode.width / 2) + 1, barcode.barHeight);
      bd = getBitmapData(sBarcode, rect);
      img = new Image();
      bitmap = new Bitmap(bd);
      img.source = bitmap;
      can.addChild(img);
      var chars:Array=text.split(" ");
      var n:String;
      var v:String;
      var total_y:Number=40;
      for(n in chars){
        v=chars[n];
        if (StringUtil.trim(v)){
          var inf:Array=v.split(":");
          var fontsize:Number=inf[1]/1.8;
          var fontheight:Number=30;
          var label:Label = new Label();
          label.text = inf[2];
          label.x = Number(inf[0])-2;
          label.y = fontheight;
          label.setStyle("fontSize", 9);
          label.setStyle("paddingBottom", 0);
          label.setStyle("paddingLeft", 0);
          label.setStyle("paddingRight", 0);
          label.setStyle("paddingTop", 0);
          can.addChild(label);
        }
      }
    }
    return can;
}

En realidad es tan sencillo como crear una imagen a partir de lo que nos devuelve la clase generadora EAN (un objeto de tipo Shape). Finalmente si queremos imprimir el código en la propia imagen tendremos que poner cada letra en el sitio que le toca, no van en cualquier lado.

¿Qué tal si hacemos el proceso contrario con un lector casero?

Os dejo el código fuente del proyecto.

Primeros pasos con Flash Media Server, creando un chat

Hoy vamos a ver cómo crear una sencilla aplicación de chat con Flex. No debe tomarse este ejemplo como una aplicación completa sino simplemente como un método para comprender cómo funcionan los distintos elementos que intervienen. Tampoco debe compararse este proyecto con el cliente IRC que os presenté hace unos meses, aunque ámbos son aplicaciones de chat,  son sistemas distintos, como veréis más adelante.

Chat en Flex con FMS

Flash Media Server (FMS, antiguamente conocido Flash Communication Server) es el software de Adobe que permite crear aplicaciones multiusuario de una manera sencilla y bajo tecnología Flash. FMS permite no sólo intercambiar y sincronizar mensajes de texto entre los clientes sino tambien audio y vídeo. Si flv es el sistema de vídeo por excelencia en Internet, FMS es la tecnología para crear sistemas multimedia multiusuario. La principal pega de FMS es que es de pago, y no precisamente barato, sin embargo existe una licencia “developer” que te permite utilizar el 100% de las posibilidades del servidor, la única limitación es la cantidad de usuarios concurrentes que puedes tener, diez en este caso. Existe una alternativa Open Source a FMS, Red5, pero no lo he utilizado nunca así que no puedo opinar. La teoría con Red5 es exactamente la misma que con FMS, sólo cambia el medio (FMS se programa con ActionScript y Red5 con Java). Como ejemplo de aplicaciones que utilizan FMS está Yahoo Live, la aplicación de grabación directa desde tu webcam de Youtube o todos, absolutamente todos los videochats que puedes encontrar en Internet.

Como lo más probable es que no tengas una licencia de FMS, trabajaremos con la versión developer que hemos indicado ya que nos permitirá poner en práctica todos los conceptos necesarios.  Lo primero que debes hacer es, por tanto, descargar e instalar FMS, hay versiones para servidores Linux y Windows, escoge la que mejor te venga. La instalación no tiene ningún secreto así que lo dejaré en vuestras manos. En mi caso, como siempre, un CentOS5.

Los proyectos creados sobre FMS son sistemas cliente/servidor y necesitan dos aplicaciones para poder funcionar:

  1. Aplicación cliente, sería la parte Flash pura y dura, la que utilizarán los usuarios.
  2. Aplicación servidor, sería lo que corre bajo FMS y a lo que se conecta la aplicación cliente.

Digamos que FMS es la varita mágina del conjunto ya que hace completamente transparente al desarrollador la conexión entre usuarios y la inyección de mensajes entre ellos. Para entenderlo pensemos en un chat donde los usuarios se conectan al servidor y cada vez que uno de ellos escribe un mensaje éste se envía automáticamente a los demás usuarios. Imagina, además, que no sólo hablamos de texto sino que cuando un usuario enciende su webcam los demás usuarios pueden verla. Todo esto lo hace FMS por ti, sólo debes preocuparte de construir tu aplicación.

Programando una aplicación FMS

Empezaremos la parte práctica creando el código necesario en el servidor. Por defecto las aplicaciones FMS de instalan bajo /opt/adobe/fms/applications creando un nuevo directorio con el nombre de tu aplicación, en nuestro caso la llamaremos textchat.

Las aplicaciones se cargan y se mantienen en memoria una vez algún usuario accede por primera vez a la misma y, cuando pasado un tiempo prudencial no hay usuarios, se descarga para liberar recursos esperando a que otro usuario vuelva a acceder. Cada vez que se hacen cambios a la parte servidor de la aplicación hay que reiniciarla, pero no es necesario reiniciar el servidor FMS para reiniciar una aplicación, basta con descargar y volver a cargar esa aplicación desde la consola de administración como veremos más adelante. Desde esta misma consola podremos ver también la salida de los comandos trace que ocurran en el servidor y los objetos creados y su contenido, ayuda imprescindible mientras estás desarrollándola. Veremos también como activar la salida de estas opciones puesto que por defecto (por seguridad) vienen deshabilitadas.

Por defecto todas las aplicaciones deben tener un archivo main.asc con el código necesario.  Crea tu main.asc de esta manera:

load("netservices.asc");

application.onAppStart = function(){
  trace("inicia text chat");
  this.users_so = SharedObject.get("users_so", false);
  this.chat_so = SharedObject.get("chat_so", false);
  this.userList = {};
  this.uid = 1;
}

application.onConnect = function(client, nick, clave, sala){
  trace(nick+" "+clave+" "+sala);
  var valido=1;
  for( i in this.userList){
    if(String(this.userList[i].nick).toLowerCase()==String(nick).toLowerCase()){
      valido=0;
      break;
    }
  }
  if(valido==1){
    client.uid = this.uid;
    client.nick = nick;
    client.sala = sala;
    this.acceptConnection(client);
    this.userList[client.uid] = client;
    this.users_so.setProperty(client.uid, client);
    var msg = "<font color='#666666'><b>*** Entra " + client.nick + " ***</b></font><br>";
    this.chat_so.send("onMsgSend", msg);
    client.call("onUserid", null, client.uid, client.nick);
    this.uid++;
  }else{
    var err = new Object();
    err.message = "Ya hay un usuario con ese nick";
    trace(err.message);
    application.rejectConnection(client, err);
  }
}

application.onDisconnect = function(oldClient){
  if(oldClient.uid!=undefined){
    trace("Descontecta cliente. UID:  " +oldClient.uid);
    var msg = "<font color='#666666'><b>*** " + oldClient.nick + " sale ***</b></font><br>";
    this.chat_so.send("onMsgSend", msg);
    for( i in application.userList){
      if(Number(application.userList[i].uid)==Number(oldClient.uid)){
        application.userList[i]=undefined;
      }
      k++;
    }
    application.disconnect(oldClient);
    application.users_so.setProperty(oldClient.uid, null);
  }
}

El funcionamiento es muy sencillo. Nuestra aplicación maneja tres eventos:

  1. onAppStart: Se dispara cuando la aplicación se carga en memoria por primera vez.
  2. onConnect: Se lanza cuando un usuario se conecta a la aplicación.
  3. onDisconnect:Cuando un usuario se desconecta.

Veamos la teoría de un chat y comprenderemos rápidamente como funciona FMS en su lado servidor. En una aplicación chat típica, el servidor debe manejar una lista de los usuarios conectados actualmente (los nicks) y debe poder ser capaz de enviar mensajes a todos los usuarios. Por ejemplo, cuando un nuevo usuario se conecta y sin que éste escriba nada, el servidor tiene que poder enviar un mensaje tipo “Nick se ha conectado al chat” a todos los demás usuarios.  Para solucionar todo esto FMS tiene lo que se conocen como objetos compartidos (SharedObjects), objetos a los que todos los clientes pueden acceder.

Si analizas ahora el código de nuestro main.asc verás que el evento onAppStart crea las variables que se utilizarán en la aplicación:

  • userList: array que contendrá la lista de usuarios conectados al sistema.
  • uid:  identificador del último usuario conectado para poder localizar de manera inequívoca a un cliente.
  • users_so: objeto compartido de la lista de usuarios
  • chat_so: objeto compartido del chat propiamente dicho, a él se envíen los mensajes que escriben los usuarios.

Creo que sobran más explicaciones ya que es tan sencillo que se entiende sólo.

Veamos ahora la teoría de qué ocurre cuando se conecta un usuario, evento onConnect. Cada vez que un cliente accede a nuestro chat debemos comprobar, antes de nada, siya hay otro usuario con el mismo nick, en cuyo caso rechazamos su conexión. Si todo va bien y su nick es único debemos guardar al usuario en la lista de usuarios y avisar a todos los clientes ya conectados que hay un nuevo usuario en el chat.

Compara ahora la teoría con el código del evento. El parámetro cliente es automático y representa al cliente que se conecta. Es un objeto con distintos parámetros predefinidos. Los demás parámetros del evento se los pasaremos nosotros desde nuestro cliente flash. Para ver si un nick ya existe simplemente lo comparamos contra todos los demás del array userList que ya hemos visto que contiene a todos los usuarios conectados. El evento onConnect puede aceptar un cliente (acceptConnection) o rechazarlo (rejectConnection).

Si el usuario ha sido admitido, le damos el siguiente UID y tendremos entonces que avisar a los demás usuarios de que tenemos un usuario nuevo. Si recordamos las variables que teníamos, había un objeto compartido al que tienen acceso todos los clientes, users_so, solo tendremos que añadir nuestro nuevo cliente a este objeto:

this.users_so.setProperty(client.uid, client);

Cuando veamos la aplicación cliente veremos como detectamos los cambios en ese objeto, pero es sumamente sencillo.

Espera!. Nos falta enviar el mensaje de “Nick se ha conectado al chat”. Tan sencillo como:

this.chat_so.send("onMsgSend", msg);

Magia!. Como chat_so era un objeto compartido, todos los clientes y el servidor tienen acceso al mismo y lo que uno escriba lo recibirán automáticamente todos los demás. Esta es la verdadera magia de FMS, no tienes que preocuparte de cómo enviar los mensajes a los usuarios uno a uno, se sincronizan sólos.

Como último detalle, cuando aceptamos la conexión de un cliente llamamos remotamente al método onUserid de la aplicación cliente. Si! has leído bien!, FMS llama a una función de tu SWF!. Más adelante veremos para qué, simplemente recuerda que cuando te conectas al chat el servidor llama a un método de la aplicación pasándole su propio UID.

Finalmente veamos el último evento del servidor, bastante obvio si has seguido lo explicado anteriormente y lo has entendido, la desconexión de un usuario.

Sin pensar mucho más entenderemos que habrá que eliminar al cliente de userList y de users_so. Pues eso mismo, no hay mucha más ciencia.

Ya tenemos la parte del servidor terminada. Sencillo ¿no?. Es posible que aún no entiendas algunas cosas. Tranquilo, cuando veamos el cliente lo entenderás todo y verás que realmente es muy sencillo crear aplicaciones de este tipo con FMS.

La consola de administración

Es una aplicación Flash desde la que podemos conectarnos como administradores al servidor FMS y realizar operaciones de mantenimiento, entre ellas cargar y descargar instancias de aplicación, ver el log del sistema (los trace), examinar objetos (en modo debug)…

Cuando instalaste FMS te pidió un usuario y una clave además de un puerto donde instalar esta consola de administración. No tienes más que conectarte a ella y comenzarás a ver estos datos.

Consola de administración de FMS

Hay, sin embargo, algunas particularidades de administración que conviene tener en cuenta. Para habilitar las opciones de debug de objetos habrá que modificar el fichero conf/_defaultRoot_/_defaultVHost_/Application.xml cambiando:

<AllowDebugDefault>false</AllowDebugDefault>

a true. Reinicias el servidor para que el cambio se active y verás algo como esto que te servirá para depurar tu aplicación.

Consola de administración de FMS

En la barra de opciones de aplicación (Live log, clients…) tienes dos pequeños botones que te permitirán recargar una instancia de la aplicación o descargarla por completo, las opciones que comentábamos antes que te ayudarán durante el desarrollo para no tener que reiniciar todo el servidor.

La aplicación cliente

Y llegamos al último punto. La aplicación, el chat, lo que ve el usuario.

Ya os he puesto una captura del resultado final. Veremos ahora los entresijos. Abajo de todo tenéis el código fuente de todo el proyecto de demostración.

Me meteré directamente al meollo de conectar con FMS y enviar y recibir datos, la interfaz de usuario y la pantalla inicial de login la dejo para vosotros. Como veréis hay una clase Chat.as que centraliza casi todo el proceso de comunicación con el servidor.

chat = new Chat();
chat.addEventListener(UserIDEvent.USER_ID, onUID);
chat.addEventListener(NCEvent.STATUS, onStatus);
chat.addEventListener(NCEvent.NONICK, onStatus);

salaSeleccionada=sala.selectedItem.toString();
chat.init("rtmp://"+host+"/textchat/"+salaSeleccionada, StringUtil.trim(nick.text), StringUtil.trim(clave.text), salaSeleccionada);

private function onUID( event:UserIDEvent ):void{
    miuid=event.userid;
    currentState = "chatState";
    msg.setFocus();
    CursorManager.removeBusyCursor();
    this.addEventListener(KeyboardEvent.KEY_UP,keyHandler);
    picker.addEventListener(DropdownEvent.CLOSE, pickerClose);   
}

private function onStatus( event:NCEvent ):void{
    CursorManager.removeBusyCursor();
    if (event.status == "closed"){
        status.text = "Desconexión completa.";
    }
    if (event.status == "nickexiste"){
        status.text = "Usuario o contraseña incorrectos.";
    }
    currentState = "";
}

Al crear la instancia de Chat haremos que se detecten un par de eventos que ejecutarán:

  •  onUID: se dispara cuando el servidor, como hemos visto antes, llama al método cliente onUserid. Nos sirve para cambiar el estado de la interfaz de usuario desde la ventana de login a la de chat ya que sólo si hemos conectado recibiremos el UID.
  • onStatus: nos ayudará adetectar conexiones rechazadas y tu propia desconexión.

Para conectar véis que llamamos al método init de Chat con cuatro parámetros, la url de la instancia de nuestra aplicación (definida como host+aplicacion+sala de chat), el nick, la clave (para un futuro) y la propia sala de chat. Si examinas este método verás qué sencillo es iniciar una conexión. El método más importante es

private function netStatus( event:NetStatusEvent ):void{
    var info:Object = event.info;
    switch(info.code)
    {
    	case "NetConnection.Connect.Success":
    	    users_so = SharedObject.getRemote("users_so", nc.uri, false);
    	    users_so.addEventListener(SyncEvent.SYNC, usersSync);
    	    users_so.addEventListener(NetStatusEvent.NET_STATUS, onSOnetStatus);
    	    users_so.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSOSecurityError);
    	    users_so.client = this;
    	    users_so.connect(nc);
    	    chat_so = SharedObject.getRemote("chat_so", nc.uri, false);
    	    chat_so.addEventListener(NetStatusEvent.NET_STATUS, onSOnetStatus);
    	    chat_so.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSOSecurityError);
    	    chat_so.client = this;
    	    chat_so.connect(nc);
    	    break;
    	case "NetConnection.Connect.Rejected":
    	    dispatchEvent( new NCEvent(NCEvent.NONICK, "nickexiste"));
    	    break;
    	case "NetConnection.Connect.Closed":
    	    dispatchEvent( new NCEvent(NCEvent.STATUS, "closed"));
    	    break;
    }
}

Es el que detecta el estado de la conexión, si el servidor nos acepta o nos rechaza tal como veíamos antes e incluso si nos desconecta. Fíjate que si somos aceptados estaremos conectándonos a los SharedObjects que creamos antes en el servidor 😉 . Ahora es Chat quien hará absolutamente todo el trabajo, la aplicacion mxml es la interface de usuario que transmite a Chat los comandos necesarios. Veamos por ejemplo como indicamos al chat que envíe a los demás usuarios un mensaje que hayamos escrito nosotros.

private function sendMsg():void{
    if ( msg.text.length > 0 ){
        chat.sendMsg(msg.text);
        msg.text = "";
    }
}

Vale sí, pero has hecho trampas, aquí estás dejando el meollo en manos de Chat 😛 . Bien, veamos qué ocurre en sendMsg.

public function sendMsg( color:String, msg:String ):void{    
    var outmsg:String = "";    
    outmsg = "<font color='#" + color + "'><b>" + this.username+":</b> " + msg + "</font><br>";
    chat_so.send("onMsgSend", outmsg);
}

¡ Sólo eso ! Como ya estamos conectados al SharedObject chat_so, le decimos a éste que llame al método onMsgSend de sus clientes y le pase el texto que hemos escrito.

De acuerdo, entonces ¿cómo leemos los mensajes que escriben los demás usuarios?. Te lo acabo de decir 😛 . Cuando un usuario escribe algo, llama (da igual cómo) a un método local de cada usuario que se encarga de escribir el texto en su ventana de chat.

public function onMsgSend( msg:String ):void{
    chatHistory += msg;        
    dispatchEvent(new Event("histChange"));
}

Obviamente este método tiene que ser público. Al despachar ese evento simplemente hace que se actualice el texto del textarea.

Hasta ahora creo que lo voy entendiendo, pero no has explicado como detectamos las entradas y salidas de usuarios. Cierto, veámoslo pues. Acabamos de ver que cuando entramos al chat y somos admitidos conectamos con los SharedObjects que creamos en el servidor. Si te has fijado (y si no hazlo ahora), al de usuarios le poníamos un listener distinto:

users_so.addEventListener(SyncEvent.SYNC, usersSync);

Esto significa que cada vez que haya un cambio en el objeto users_so todos los clientes serán notificados de ello. Por ejemplo, cada vez que entra o sale un usuario vimos que añadíamos o quitábamos un elemento de este objeto que representaba a ése usuario. Cada una de estas operaciones hará que nuestra aplicación cliente reciba una llamada a usersSync.

private function usersSync(event:SyncEvent):void{
    var results:Object = event.target.data;
    var usersArray:Array = new Array();
    var existe:Boolean=false;
    //debo actualiza solo los datos
    for( var i:String in results ){
        if(results[i].sala==this.sala){
            existe=false;
            for(var j:Number=0; j<dpUsers.length; j++){
                if(dpUsers[j].uid==results[i].uid){
                    existe=true;
                    break;
                }
            }

            if(existe){
                //por si se ha cambiado el nick
                dpUsers[j].nick=results[i].nick;
            }else{
                var clientObj:Object=new Object();   
                clientObj.nick = results[i].nick;
                clientObj.uid = results[i].uid;
                dpUsers.addItem(clientObj);
            }
        }
    }
    //ahora tengo que eliminar las bajas
    //elimino empezando por la ultima para no cambiar los indices
    for(j=dpUsers.length-1; j>=0; j--){
        existe=false;
        for(i in results ){
            if(dpUsers[j].uid==results[i].uid && results[i].sala==this.sala){
                existe=true;
            }
        }

        if(!existe){
            dpUsers.removeItemAt(j);
        }
    }
    //finalmente ordenamos los usuarios
    var sort:Sort = new Sort();
    sort.fields = [new SortField("nick",true)];
    dpUsers.sort = sort;
    dpUsers.refresh();
    dispatchEvent(new Event("dpChange"));           
}

Esta método repasará todos los usuarios del SharedObject comparándolos con los de la lista de usuarios del canal donde nos encontramos chateando y añadiendo los nuevos usuarios y eliminando los que hayan salido.  Quizás no sea el mejor método para manejar los usuarios y habría sido mucho más sencillo tener una pareja de métodos addUsuario y delUsuario que manejen todo automáticamente con llamadas remotas igual que con los mensajes, pero quería que viésemos el modo de recibir avisos de cambios en un SharedObject.

Conclusiones

Hemos visto cómo de una manera sencillísima podemos crear nuestro propio chat sin excesivas complicaciones. Piensa además que esto es, seguramente, lo más sencillo, el primer paso con FMS, las posibilidades reales son infinitas.

Quizás en otros artículos ampliemos las posibilidades de este pequeño ejemplo añadiendo vídeo y audio o, incluso, hagamos un pequeño sistema de grabación de vídeo con tu webcam similar al de Youtube.

Como siempre, aquí os dejo el código completo del proyecto. Recuerda que es un ejemplo de lo que se puede hacer, no debes usarlo tal cual sino aplicar lo que hemos explicado. Del mismo modo habrá cosas que no sean perfectas y se puedan hacer mejor, no lo dudo, mi intención era explicar los conceptos básicos de FMS y que se vea su potencial.