Category Archives: Web

Arrastrar y soltar con HTML5

Introducción.

En este artículo se revisarán los pasos necesarios para implementar la facilidad de arrastrar y soltar (drag and drop) en una página web aprovechando las funcionalidades provistas por HTML5.  Para este desarrollo se utilizará jQuery para facilitar la implementación, sin embargo esto no es estrictamente necesario.

Establecer los elementos "arrastrables".

El primer paso consiste en establecer cuales elementos podrán ser arrastrados, para hacer esto se les deberá agregar el atributo draggable='true'.  En el caso del ejemplo, los elementos arrastrables son imágenes de la siguiente manera.

<img id="tool0" draggable="true" title="tool" src="img/tool0.png" alt="tool" />
<img id="tool1" draggable="true" title="tool" src="img/tool1.png" alt="tool" />
<img id="tool2" draggable="true" title="tool" src="img/tool2.png" alt="tool" />

Definir el comportamiento de los elementos "arrastrables".

El siguiente paso es establecer que se debe hacer cuando los objetos arrastrables son manipulados, es decir, manejar sus eventos.  Estos eventos corresponden con los siguientes y su implementación se debe definir cuando la estructura de la página se encuentra completamente cargada.

 dragstart El elemento se empieza a arrastrar.
 drag El elemento está siendo arrastrado.
 dragend El elemento ha dejado de ser arrastado.

Durante el manejo del evento dragstart se realizan las siguientes tareas.

  1. Almacenar la información del elemento arrastrable (su id en este caso) necesaria para ser procesado en caso de ser soltado exitosamente.
  2. Agregar una nueva clase CSS (itemSelected) para modificar su apariencia mientras es arrastrado.
  3. Modificar la imagen del elemento arrastrado la cual por defecto es la del mismo elemento.
$('.page #main #items .item').bind('dragstart', function(event) {
    // Store information about the item dragged.
    event.originalEvent.dataTransfer.setData("text/plain", event.target.getAttribute('id'));
    // Add a CSS class to highlight the dragged item.
    $(event.target).addClass('itemSelected');
    // Set an image as drag icon.  The image was preloaded.
    var dragIcon = document.createElement('img');
    dragIcon.src = 'img/hand.png';
    event.originalEvent.dataTransfer.setDragImage(dragIcon, 139, 43);
});

Durante el manejo del evento drag no se realizan tareas especiales en este caso.

$('.page #main #items .item').bind('drag', function(event) {
    // The item is being dragged!
});

Durante el manejo del evento dragend se realiza una única tarea y corresponde con retirar la clase CSS añadida durante dragstart para indicar que el elemento ya no está siendo arrastrado actualmente.

$('.page #main #items .item').bind('dragend', function(event) {
    // Remove the CSS class that highlights it while beign dragged.
    $(event.target).removeClass('itemSelected');
});

Definir el comportamiento de las "zonas de descarga".

El paso final consiste en establecer que se debe hacer cuando los objetos arrastrables interactúan con las zonas de descarga (drop zones), es decir, manejar sus eventos.  Estos eventos corresponden con los siguientes y su implementación también se debe definir cuando la estructura de la página se encuentra completamente cargada.

 dragenter El elemento arrastrable ha entrado a una zona de descarga.
 dragleave El elemento arrastrable ha salido de una zona de descarga.
 dragover El elemento arrastrable está siendo arrastrado sobre una zona de descarga.
 drop El elemento arrastrable ha sido soltado sobre una zona de descarga.

Durante el manejo del evento dragenter se agrega una nueva clase CSS (itemOnDropArea) para modificar la apariencia de la zona de descarga mientras un elemento es arrastrado sobre ella.

$('.page #main #boxes .box').bind('dragenter', function(event) {
    // Add a CSS class to highlight the drop zone used.
    $(event.target).addClass('itemOnDropArea');
    // Prevents the default event that denies the DnD
    // behaviour.  Acts as "event.preventDefault()" in
    // vanilla Javascript.
    return false;
});

Durante el manejo del evento dragleave se realiza la tarea contraria, se remueve la clase CSS agregada durante el suceso del evento anterior para mostrar que ningún elemento es arrastrado ahora sobre la zona de descarga.

$('.page #main #boxes .box').bind('dragleave', function(event) {
    // Remove the CSS class that highlights the used drop zone.
    $(event.target).removeClass('itemOnDropArea');
    // Prevents the default event that denies the DnD
    // behaviour.  Acts as "event.preventDefault()" in
    // vanilla Javascript.
    return false;
});

Durante el manejo del evento dragover no se realiza en este caso ninguna tarea interesante además de indicarle al navegador que evite su comportamiento por defecto el cual es impedir que se realice.

$('.page #main #boxes .box').bind('dragover', function(event) {
    // Prevents the default event that denies the DnD
    // behaviour.  Acts as "event.preventDefault()" in
    // vanilla Javascript.
    return false;
});

Durante el manejo del evento drop, que sucede cuando el elemento arrastrable se suelta sobre la zona de descarga, se realizan las siguientes tareas en la aplicación de demostración.

  1. Obtener la información almacenada en el objeto dataTransfer definida durante el manejo del evento dragstart.
  2. Obtener la información del objeto arrastrable.
  3. Obtener la información de la zona de descarga.
  4. Determinar si la descarga del objeto es válida o no en la zona de descarga específica.
  5. Realizar una acción según se determine en la tarea anterior: remover el elemento o mostrar un mensaje de error.
  6. Evitar que se efectúe el comportamiento por defecto relacionado con el evento.
$('.page #main #boxes .box').bind('drop', function(event) {
	// Get the stored information about the draggable item,
	// the element ID in this case.
	var itemId = event.originalEvent.dataTransfer.getData("text/plain");
	// Get the type of the draggable item, stored in the
	// ALT attribute in this case.
	var itemType = $("#" + itemId).attr('alt');
	// Get the drop zone's ID to difference the type of it.
	var dropboxType = $(event.target).attr('id');
	// Check if the draggable item can be or not dropped
	// into the current drop zone.
	if(dropboxType == 'toolbox')
	{
		if(itemType == 'tool')
			$("#" + itemId).remove();
		else
			alert("That is a FRUIT, put it on the Fruit's Basket!");
	}
	if(dropboxType == 'fruitbasket')
	{
		if(itemType == 'fruit')
			$("#" + itemId).remove();
		else
			alert("That is a TOOL, put it on the Toolbox!");
	}
	// Remove the CSS class that highlights the used drop zone.
	$(event.target).removeClass('itemOnDropArea');
	// Prevents the default event that denies the DnD
	// behaviour.  Acts as "event.preventDefault()" in
	// vanilla Javascript.
	return false;
});

El prototipo.

En este prototipo se utilizan los fragmentos de código mostrados en este artículo, en él se muestran en la parte inferior dos series de objetos arrastrables (herramientas y frutas) y en la parte superior dos zonas de descarga (una caja de herramientas y una canasta de frutas).  La página web permitirá que el usuario arrastre los elementos a cualquiera de de las zonas de descarga, sin embargo sólo aceptará que se almacenen en la zona correcta.  En caso de intentarse hacer lo contrario se obtendrá un mensaje de error.

Prototipo de demostración de arrastrar y soltar con HTML5

Prototipo de demostración de arrastrar y soltar con HTML5

Enlaces.

Convertir coordenadas geográficas grados-minutos-segundos a notacional decimal (3)

Introducción.

Después de encontrar dos métodos (A y B) para la conversión de coordenadas entre formatos decimal y grados, minutos y segundos, encontré en el libro HTML5 Geolocation de Anthony T. Holdener III la implementación de dos funciones en Javascript que realizan con precisión este procedimiento y que me parecieron muy intersantes.  Este corto libro es en general muy interesante y recomiendo su lectura.

Conversión de coordenadas: formato decimal a grados-minutos-segundos.

/**
 * Convierte una coordenada en formato decimal a su correspondiente
 * versión en formato grados-minutos-segundos.
 *
 * @param	Float	Valor real de la coordenada.
 * @param	Int	Tipo de la coordenada {Coordenada.LATITUD, Coordenada.LONGITUD}.
 * @return	Array	['grados', 'minutos', 'segundos', 'direccion', 'valor'].
 */
function dec2gms(valor, tipo)
{
	grados    = Math.abs(parseInt(valor));
	minutos   = (Math.abs(valor) - grados) * 60;
	segundos  = minutos;
	minutos   = Math.abs(parseInt(minutos));
	segundos  = Math.round((segundos - minutos) * 60 * 1000000) / 1000000;
	signo     = (valor < 0) ? -1 : 1; 	direccion = (tipo == Coordenada.LATITUD) ?
                    ((signo > 0) ? 'N' : 'S') :
	            ((signo > 0) ? 'E' : 'W');
	if(isNaN(direccion))
		grados = grados * signo;
	return {
		'grados'   : grados,
		'minutos'  : minutos,
		'segundos' : segundos,
		'direccion': direccion,
		'valor'    : grados + "\u00b0 " + minutos + "' "+ segundos +
		             "\"" + ((isNaN(direccion)) ? (' ' + direccion) : '')
	};
}

Por ejemplo.

var x = 48.853;
r1 = dec2gms(x, Coordenada.LATITUD);
alert('Conversion dec2gms = ' + r1.valor);

Conversión de coordenadas: formato grados-minutos-segundos a decimal.

/**
 * Convierte una coordenada en formato grados-minutos-segundos a su
 * correspondiente versión en formato decimal.
 *
 * @param	Float	Grados de la coordenada.
 * @param	Float	Minutos de la coordenada.
 * @param	Float	Segundos de la coordenada.
 * @param	String	Sentido de la coordenada {Coordenada.NORTE,
					Coordenada.SUR, Coordenada.ORIENTE,
					Coordenada.OCCIDENTE}
 * @return	Array	['decimal', 'valor'].
 */
function gms2dec(grados, minutos, segundos, direccion)
{
	if(direccion)
	{
		signo     = (direccion.toLowerCase() == 'w' ||
		             direccion.toLowerCase() == 's') ?
		            -1 : 1;
		direccion = (direccion.toLowerCase() == 'w' ||
		             direccion.toLowerCase() == 's' ||
		             direccion.toLowerCase() == 'n' ||
		             direccion.toLowerCase() == 'e') ?
		            direccion.toLowerCase() : '';
	}
	else
	{
		signo     = (grados < 0) ? -1 : 1;
		direccion = '';
	}
	dec = Math.round((Math.abs(grados) + ((minutos * 60) + segundos) / 3600) * 1000000) / 1000000;
	if(isNaN(direccion) || direccion == '')
		dec = dec * signo;
	return {
		'decimal': dec,
		'valor'  : dec + "\u00b0" + ((isNaN(direccion) || direccion == '') ? (' ' + direccion) : '')
	};
}

Por ejemplo.

var xg = 48;
var xm = 51;
var xs = 10.8;
r2 = gms2dec(xg, xm, xs, Coordenada.NORTE);
alert('Conversion gms2dec = ' + r2.valor);

Código fuente y demostración.

Obtenga el código fuente del artículo junto con la ejecución de la demostración en la siguiente ubicación.

http://demo.jorgeivanmeza.com/JavaScript/CoordConverter/0.3/

Como acceder a la geolocalización desde Javascript

Introducción.

La geolocalización es la posibilidad de conocer cual es la ubicación física de un dispositivo.  Esta ubicación es geográfica, es decir, se define especialmente mediante sus correspondientes valores de latitud y longitud.

Los dispositivos pueden obtener esta información desde diferentes fuentes, las cuales estarán relacionadas a su vez con precisión de estos datos.  Los orígenes mas comúnes para la información de geolocalización de un dispositivo son los siguientes.

  1. Utilizando un GPS (Global Positioning System).
  2. Mediante su identificación en celdas GSM/CDMA.
  3. Mediante su dirección IP.
  4. Mediante la dirección MAC de WiFi y Bluetooth.
  5. Especificado por el usuario manualmente.

Estos métodos se encuentran disponibles para las aplicaciones nativas que pueden acceder libremente al hardware del dispositivo, sin embargo ahora también es posible acceder a esta información desde una aplicación web utilizando Javascript gracias a la integración del API de geolocalización de la W3C en los navegadores mas recientes (incluyendo los móviles).

  • Firefox (3.5+)
  • Chrome (5.0+)
  • Internet Explorer (9.0+)
  • Safari (5.0+)
  • Opera (10.6+)
  • iPhone (3.1+)
  • Android (2.0+)
  • BlackBerry (6+)

Para dar soporte a navegadores antíguos se requiere que se utilice una capa adicional como Google Gears que supla la falencia de esta tecnología.

En este artículo se explicará el API necesario para acceder al API de geolocalización mediante el uso del lenguaje Javascript utilizando un navegador moderno como los mencionados.

Verificar el soporte

El primer paso debe ser siempre verificar si el navegador web en el cual se despliega la aplicación tiene o no el soporte para esta tecnología, a partir de este resultado se deberá decidir que hacer.

if(navigator.geolocation)
{
    // Se cuenta con el soporte para geolocalización, entonces ...
}
else
{
    // No se cuenta con soporte para geolocalización, manejar la situación.
}

Solicitar la ubicación al navegador.

El siguiente paso consiste directamente en solicitarle al navegador web cual es la ubicación del dispositivo.  El llamado a esta función es asíncrono, es decir, no retorna inmediatamente un valor sino que invoca a una función específica (callback) cuando este se obtiene.  Así mismo, como el conocer la ubicación de un dispositivo (y por ende su usuario) tiene implicaciones en la privacidad de las personas, la especificación dispone que el usuario esté siempre facultado para aceptar o negar el acceso a esta información.  Por este motivo los navegadores desplegarán barras como la mostrada a continuación solicitando la confirmación final.

Permitir el acceso a la información de geolocalización en Chrome

Permitir el acceso a la información de geolocalización en Chrome

A continuación se muestra la invocación general de la función para obtener la geolocalización del dispositivo.

navigator.geolocation.getCurrentPosition(onSuccessGeolocating,
                                         onErrorGeolocating,
                                         options);

Los dos primeros parámetros corresponden a funciones Javascript que se ejecutarán según se tenga éxito o no durante el proceso de geolocalización (se analizarán a continuación), el tercer parámetro es opcional y corresponde con uno o varios de los modificadores descritos.

OpciónTipoValor por defectoDescripción
enableHighAccuracyBooleanofalseIntenta obtener la información mas precisa que sea posible.  Debe tenerse en cuenta que esta opción también toma mas tiempo y probablemente consume una mayor cantidad de batería del dispositivo.
maximumAgeEntero 0Acepta como resultado el último valor histórico siempre y cuando este no sea mas antíguo que el valor especificado en milisegundos.
timeoutEntero 0Espera máximo la cantidad de milisegundos especificada antes de abortar el procedimiento.

A continuación se muestra un ejemplo de implementación de esta función.  En este se definen las funciones onSuccessGeolocating y onErrorGeolocating para determinar la implementación del manejo de éxito y error respectivamente de la geolocalización, y define además que se intente realizar el procedimiento con la mayor precisión posible, se acepten valores en caché con máximo 5 segundos de antiguedad y se espere máximo 10 segundos para terminar el procedimiento.

navigator.geolocation.getCurrentPosition(onSuccessGeolocating,
                                         onErrorGeolocating,
                                         {
                                       		enableHighAccuracy: true,
                                       		maximumAge:         5000,
                                       		timeout:            10000
                                         });

Establecer que hacer con la información de geolocalización.

Si el procedimiento fue exitoso, se define la función onSuccessGeolocating referenciada en el paso inmediatamente anterior, en ella se establece que se deberá hacer con la información obtenida.

function onSuccessGeolocating(position)
{
    // ...
}

El objeto Position que se recibe a través de los parámetros de la función incluye la siguiente información.

AtributoDescripción
coordsInformación de la ubicación obtenida
timestampInformación del momento en que fue obtenida la información

A su vez, el objeto Coordinates incluye la siguiente información en su interior.

AtributoDescripción
latitudeValor de la latitud de la ubicación del dispositivo medida en grados decimales
longitudeValor de la longitud de la ubicación del dispositivo medido en grados decimales
altitudeValor de la altura geográfica de la ubicación del dispositivo medida en metros
accuracyPrecisión de la latitud y longitud medida en metros
altitudeAccuracyPrecisión de la altitud medida en metros
headingDirección en la que se mueve el dispositivo. Medida en grados desde 0 hasta 360. Su valor será NaN cuando el dispositivo se encuentra inmóvil o null si esta característica no es soportada
speedValor de la longitud de la ubicación del dispositivo

Esto significa que la información básica de la ubicación del dispositivo esta finalmente almacenada en position.coords.latitude y position.coords.longitude.

Establecer que hacer en caso de error.

En algunas ocasiones la geolocalización puede fallar, por ejemplo cuando se utiliza el GPS pero no se cuenta con la información de suficientes satélites o cuando el usuario impide el procedimiento.  Para manejar esta conducta se define la función onErrorGeolocating referenciada durante la invocación de navigator.geolocation.getCurrentPosition.

function onErrorGeolocating(error)
{
    // ...
}

La información del error puede obtenerse del objeto PositionError recibido por parámetro el cual cuenta con los siguientes atributos en su interior.

AtributoDescripción
codeCódigo numérico que referencia al tipo de error sucedido.  Su valor corresponde con una de las siguientes constantes 

PERMISSION_DENIED: no se permitió o no se tienen suficientes privilegios para acceder al servicio de geolocalización.

POSITION_UNAVAILABLE: el dispositivo no pudo determinar correctamente su ubicación.

TIMEOUT: el intento de geolocalización tomó mas tiempo del permitido (opción timeout).

messageTexto que describe el error sucedido.  Este mensaje se utiliza para depuración en desarrollo, no se debe utilizar para mostrarse al usuario final.

A continuación se muestra un ejemplo básico de implementación de esta función.

function onErrorGeolocating(error)
{
	switch(error.code)
	{
		case error.PERMISSION_DENIED:
			alert('ERROR: User denied access to track physical position!');
		break;
		case error.POSITION_UNAVAILABLE:
			alert("ERROR: There is a problem getting the position of the device!");
		break;
		case error.TIMEOUT:
			alert("ERROR: The application timed out trying to get the position of the device!");
		break;
		default:
			alert("ERROR: Unknown problem!");
		break;
	}
}

El prototipo.

Como demostración de esta API se incluye el siguiente prototipo el cual intenta obtener la ubicación actual del usuario y desplegarla en un mapa de Google Maps.  Se recomienda consultar su código fuente para complementar los contenidos de este artículo.

 

Demostración de geolocalización con Javascript

Demostración de geolocalización con Javascript

Enlaces.

Evitando el reemplazo de los guiones y comillas dobles en WordPress

Introducción.

WordPress tiene una característica que consiste en reemplazar ciertos carácteres o cadenas en sus correspondencias mas bonitas, por ejemplo reemplaza los guiones dobles por un único guión mas largo que el normal.  Esto lo hace para embellecer los textos que se publican.

Este loable objetivo puede ser interesante en la mayoría de los casos pero es un total infortunio para quienes publicamos código fuente o textos para ejecutarse en la línea de comando los cuales deberán transcribirse de manera fiel o simplemente fallarán debido a que cualquier variación como las propuestas les harán perder su validez sintáctica.

Hace un tiempo encontré como solución modificar las líneas exactas en las cuales WordPress realiza esta inoportuna conversión, sin embargo estas líneas pertenecen a un archivo del núcleo de WordPress y desafortunadamente es sobreescrito durante las actualizaciones de WordPress y por ende las modificaciones se pierden con la misma regularidad.

Para solventar esto he encontrado una solución similar que se basa en la adaptación de un plugin ya existente, así que las modificaciones perdurarán a las actualizaciones de WordPress y sólo deberán repetirse ante una posible actualización del plugin, las cuales por supuesto suceden con una frecuencia mucho menor.

Procedimiento.

Instalar y activar el plugin de WordPress llamado untexturize.

Editar el contenido del archivo wpuntexturize.php.  Por facilidad se recomienda utilizar el mismo editor de Plugins que provee WordPress.

Identificar el código fuente de la función c2c_wpuntexturize y modificar su contenido de la siguiente manera.

$char_codes = array( '&#8216;', '&#8217;', '&#8220;', '&#8221;', '&#8242;', '&#8243;',
                     '&#8212;', ' &#8212; ', '&#8211;', ' &#8211; ', 'xn--');   // nuevo.
$replacements = array( "'", "'", '"', '"', "'", '"',
                       '---', ' -- ', '--', ' - ', 'xn&#8211;');    // nuevo.
return str_replace( $char_codes, $replacements, $text );

Si se nota, las líneas marcadas con el comentario nuevo han sido agregadas con respecto al código fuente original del plugin, estas líneas corresponden con la información de los reemplazos de los guíones facultando al plugin a regresarlos a su presentación original mas allá de sólo las comillas dobles las cuales son la funcionalidad propia del complemento.

A partir del momento en que se realicen estas modificaciones, las entradas del blog mostrarán correctamente las comillas y guíones dobles en sus textos.

Enlaces.

Google Chrome y los puertos inseguros: ERR_UNSAFE_PORT

Introducción.

Aparentemente Google Chrome incluye características de seguridad obligatorias como esta que impide que el usuario acceda a servidores a través de ciertos puertos considerados inseguros aunque eso sea realmente lo que se desea.

Error puertos inseguros de Google Chrome

Error puertos inseguros de Google Chrome

A continuación se define el procedimiento necesario para indicarle a Chrome que efectivamente se desea acceder a esos puertos.

Procedimiento.

La única opción es indicarle a Chrome desde el momento de su ejecución cuales son los puertos que se consideran seguros para permitir conexiones mas allá de los estándar.  Para hacer esto es necesario indicar el parámetro –explicitly-allowed-ports seguido por la lista de puertos separados por comas.  Por ejemplo:

$ /usr/bin/google-chrome –explicitly-allowed-ports=4444,5555,6666

Por facilidad, especialmente en la plataforma Windows, se recomienda modificar el acceso directo a la aplicación para que la modificación perdure.

Primeros pasos con PhoneGap para Android

Introducción.

PhoneGap en pocas palabras es un framework para el desarrollo de aplicaciones móviles que posibilita a los desarrolladores a que implementen sus proyectos utilizando las tecnologías estándar de web: HTML5, CSS3 y Javascript, y este las convierte a aplicaciones híbridas, es decir, aplicaciones nativas de las diferentes plataformas móviles existentes que tienen acceso a gran parte del API nativo.

Tomado de http://www.phonegap.com/

Tomado de http://www.phonegap.com/

Esto lo logra empaquetando la aplicación web original con un navegador basado en webkit para desplegarla como si fuera una aplicación verdaderamente nativa.

Actualmente este framework soporta seis de las principales plataformas móviles del mercado: iOS, Android, Blackberry, PalmOS, Windows Mobile y Symbian.  Para mas información acerca del estado actual del soporte en cada una de estas plataformas consultar las características soportadas.

Existen otros frameworks similares a este entre los que se destacan Appcelerator Titanium, Mobl y Sencha Touch de los cuales espero estar escribiendo mas adelante.

En este artículo se describirá el proceso de instalación de PhoneGap, la creación de un proyecto base para el desarrollo con este framework y la elaboración de un ejemplo simple.

Instalar PhoneGap.

Descargar la última versión disponible del framework desde la siguiente ubicación.

https://code.google.com/p/phonegap/downloads/list

Para efectos de la documentación se utilizará la versión 0.9.5.1 que corresponde con las mas reciente para esta fecha.

$ wget http://phonegap.googlecode.com/files/phonegap-0.9.5.1.zip

Se descomprime el paquete recién descargado y se mueve a su ubicación final.

$ unzip phonegap-0.9.5.1.zip

$ mkdir ~/phonegap

$ mv phonegap-0.9.5.1 ~/phonegap/0.9.5.1

Crear la plantilla base de un proyecto Android.

A continuación se relacionan los pasos que se deben realizar para crear un proyecto PhoneGap para Android utilizando Eclipse y el plugin ADT instalados anteriormente.

Iniciar Eclipse y crear un nuevo proyecto a través del menú File > New > Android Project.

Crear un nuevo proyecto Android

Crear un nuevo proyecto Android

En el diálogo de información del proyecto a crearse especificar al menos los siguientes campos y presione el botón Finish para continuar.

  1. Nombre del proyecto (project name).
  2. API de Android a utilizarse (build target).  En este caso se utilizará el API 2.2.
  3. Nombre de la aplicación (application name).
  4. Crear una actividad (create activity).
Información básica del proyecto Android

Información básica del proyecto Android

En el Explorador de Paquetes (package explorer)  de Eclipse crear bajo el proyecto una carpeta /assets/www y otra /libs.

Copiar en la carpeta /assets/www el archivo phonegap.0.9.5.1.js y copiar en /libs el archivo phonegap.0.9.5.1.jar.  Ambos archivos se encuentran bajo el directorio ~/phonegap/0.9.5.1/Android creado durante el paso de instalación anterior.

Estructura del proyecto Android

Estructura del proyecto Android

Hacer clic derecho sobre el directorio /libs y seleccionar el menú Build Path > Configure Build Path… Allí en la pestaña Libraries agregue la referencia a /libs/phonegap.0.9.5.1.jar presionando el botón Add JARs…

Agregar al proyecto la referencia al JAR de PhoneGap.

Agregar al proyecto la referencia al JAR de PhoneGap.

Realizar las siguientes modificaciones al código fuente de la actividad.  Este archivo se ubica bajo la carpeta /src del proyecto (/src/com.jimezam.phonegap.demo/App.java en este caso).

  1. Reemplazar la línea 3 (import android.app.Activity;) con la siguiente: import com.phonegap.*;
  2. En la línea 6 cambiar la superclase de App de Activity a DroidGap.
  3. Reemplazar la línea 11 (setContentView(R.layout.main);) con la siguiente: super.loadUrl("file:///android_asset/www/index.html");
Modificaciones a la actividad inicial.
Modificaciones a la actividad inicial.

Hacer clic derecho sobre el archivo AndroidManifest.xml y seleccionar el menú Open With… > Text Editor.  A este documento realizar las siguientes modificaciones.

1. Agregar el siguiente texto entre la apertura de la etiqueta <manifest> y la apertura de la etiqueta <application>.

<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:resizeable="true"
android:anyDensity="true"
/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

2. Agregar el siguiente atributo a la etiqueta <activity>.

android:configChanges="orientation|keyboardHidden"

Modificaciones al documento AndroidManifest.xml.

Modificaciones al documento AndroidManifest.xml.

Finalmente crear el archivo /assets/www/index.html con el código fuente para la demostración.

<!DOCTYPE HTML>
<html>
<head>
<title>PhoneGap demostration with Android</title>
<script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
</head>
<body>
<h1>Hello Android's World with PhoneGap</h1>
</body>
</html>

Ejecutar el proyecto en el emulador.

Seleccionar el menú Run > Run As > Android Application.

Ejecutar como aplicación Android.

Ejecutar como aplicación Android.

Estos son un par de ejemplos de aplicaciones web simples ejecutándose con PhoneGap en el emulador de Android.

Hola Mundo web con Android

Hola Mundo web con Android

The Simple List 0.1

The Simple List 0.1

Enlaces.

Experimentando con jQuery Mobile: The Simple List 0.1

Introducción.

Después de experimentar con jQTouch durante la implementación de un prototipo de aplicación para el 6CCC, me ha tomado unas cuantas horas para elaborar un nuevo prototipo de aplicación web móvil, esta vez utilizando jQuery Mobile como framework para el nivel de presentación.

Su uso es muy similar al visto anteriormente, sin embargo considero que este proyecto cuenta con mejor documentación que el primero.  Ambos se basan en la mejora progresiva (Progresive Enhancement) y en la degradación aceptable (Graceful degradation) lo que los hace fáciles de entender por parte de los desarrolladores que estén habituados al uso de HTML/CSS/Javascript.

El prototipo.

Esta aplicación permite administrar una lista simple de elementos al estilo de las cosas por hacer.  La idea con este prototipo es el experimentar el uso del framework jQuery Mobile.

Para la persistencia se utiliza la facilidad del LocalStorage provisto por HTML5 y que es soportado por los navegadores web modernos incluyendo los incorporados en los sistemas operativos de los dispositivos móviles.

Estas son a grandes razgos las funcionalidades y flujos de información del prototipo.

The Simple List 0.1

The Simple List 0.1

 

Estas son algunas de las pantallas (page) de la aplicación web.

El prototipo puede probarse en línea a través de la web (ver sección de enlaces) y ya que su código fuente se encuentra completamente documentado, servirá también como ejemplo práctico para quienes estén interesados en aprender a desarrollar utilizando este framework web.

Enlaces.

Aplicación para el 6CCC: cómo llego ? Version web móvil

Introducción.

Otro prototipo que estuve preparando para el sexto Congreso Colombiano de Computación fue una aplicación web móvil basada en jQTouch para facilitarle a los visitantes consultar fácilmente la agenda de las conferencias y obtener la ruta que se deberá recorrer para llegar a los lugares del evento o demás sitios de interés de la ciudad.

El prototipo.

  • Como se mencionó, se desarrolló utilizando jQTouch el cual es un plugin de jQuery por ende, la mayor parte de la aplicación se encuentra desarrollada en Javascript.
  • La estructura de la información se encuentra implementada en HTML5 (sin validar aún).
  • Se utilizó el servicio de mapas y rutas de Bing Maps ya que según los requerimientos de esta aplicación Bing cuenta con mejor información de Colombia que los demás proveedores.
  • La aplicación realiza la geocodificación de la ubicación del usuario, intentando identificar la dirección en que se encuentra.
  • También permite realizar la geocodificación inversa (de dirección a posición geográfica) según solicitud del usuario.  En este caso parece que la calidad del servicio de Bing es inferior a la de Google y valdría la pena cambiar al proveedor de este servicio específico.
  • Por restricciones de seguridad de los navegadores web modernos, es necesario que el usuario autorice el acceso al GPS por parte de la aplicación web.  Si esto no se realiza, la aplicación no podrá contar con la posición del usuario en la ciudad.
  • Para la demostración se manipuló el código de la aplicación para que en el momento de trazar una ruta siempre se tome una ubicación en Manizales (Caldas) como posición del usuario y permitir así experimentar con esta funcionalidad del prototipo.

Conclusiones.

  • Por falta de tiempo no pudo hacerse pública la aplicación web móvil ya que no fue posible realizar suficientes pruebas con dispositivos móviles reales.
  • Es necesario investigar mas acerca de jQTouch para utilizarlo en aplicaciones reales en producción.  Hasta el momento me parece una opción muy interesante para el desarrollo fácil de aplicaciones web móviles sin embargo hasta el momento no he encontrado buena documentación al respecto.
  • La mayor parte del prototipo se encuentra lista, espero en un futuro cercano realizar mayores pruebas y ajustes para publicarlo como una versión final.

Enlaces.

Aplicación para el 6CCC: nuevo estilo para la programación

Introducción.

Esta es una aplicación muy sencilla que se desarrolló en PHP para el sexto Congreso Colombiano de Computación.  Todo comenzó cuando no me resultó cómoda la manera como el sitio web desplegaba la programación del evento: por bloques de auditorios siendo las filas conferencias y las columnas los días de las mismas.

Programación del 6CCC (versión original)

Programación del 6CCC (versión original)

Este estilo de presentación no es conveniente ya que no me permite visualizar fácilmente que conferencias se están realizando en un momento dado para poder decidir a cual de ellas asisto, es decir, el estilo idóneo de presentación de las conferencias debería ser en el que cada bloque representa un día del evento, cada fila un rango de tiempo específico y cada columna un auditorio donde se estén realizando las conferencias.  De esta manera, una vez ubicado el rango de tiempo que se desea consultar sólo será necesario revisar las columnas para determinar las conferencias a las que se puede asistir.

Ya que manipular manualmente esta información  para poder determinar cuales eran las conferencias que quería ver era demasiado engorroso decidi implementar una solución computacional muy simple.  Inicialmente la iba a desarrollar en Javascript por completo manipulando el árbol DOM pero me dí cuenta que podría serle de utilidad a otras personas también y preferí no sobrecargar al navegador del cliente con las operaciones de transformación del contenido del programa.  El resultado final fue el siguiente.

 

Programación del 6CCC (versión mejorada)

Programación del 6CCC (versión mejorada)

El prototipo.

Las premisas eran las siguientes.

  • No sobrecargar al cliente con cálculos en Javascript para facilitar la consulta desde móviles.
  • No requerir que se ingrese la información de la programación nuevamente, se debería tomar de la programación existente directamente.
  • Actualizarse automáticamente a la par de la programación original (esta última debería respetar su estructura HTML).
  • Facilitar la lectura de la información.
  • Facilitar la consulta de las conferencias presentándose en un momento específico del día y cuales se están presentando en este mismo momento.
  • Permitir la personalización de la presentación (esto se obtuvo mediante la implementación de una vista independiente de la lógica de procesamiento y de clases CSS).

El prototipo se implementó en PHP sin ningún tipo de framework especializado y utilizando la técnica de web scraping para obtener directamente la información de la página web original de la programación, de ahí que fuera necesario que esta respetara su estructura HTML durante las actualizaciones.  Para esto se utilizó la librería phpQuery la cual permite entre otras cosas obtener secciones de código HTML mediante rutas de selectores CSS de manera similar a jQuery.

Conclusiones.

  • El uso de phpQuery para las necesidades del prototipo no fue tan simple ni intuitivo como lo es el uso de jQuery, sin embargo una vez entendida su implementación, permitió desarrollar la funcionalidad necesaria.
  • La programación original contaba con múltiples inconsistencias: diferentes formatos de fecha, la presencia de un día adicional en la programación general, la presencia de las temáticas generales de los bloques de conferencias en el mismo contenido de las conferencias, conferencias sin hora específica y, filas y columnas vacías para generar espacios en las tablas.  Por este motivo el prototipo incluye varias validaciones para solventar estos problemas que dificultan la adquisición de la información.
  • El prototipo toma la fecha y hora del sistema para determinar las conferencias que se están presentando actualmente (tanto para resaltarlas en el listado como para mostrarlas al inicio de la programación).  No se tuvieron en cuenta diferencias en la zona horaria.
  • El prototipo no pudo ser incluído en el sitio web del congreso ya que fue desarrollado utilizando PHP 5.3 (utiliza una función anónima en PHP) mientras que el hosting utilizado por la página era 5.2 y no se contaba con la información necesaria para convertir la sintáxis de la función anónima al estilo antiguo.
  • El código fuente del prototipo fue liberado bajo la licencia Creative Commons Attribution 3.0.

Enlaces.

Instalando Google Chrome en GNU/Linux Ubuntu 11.04 (y otros)

Introducción.

Google Chrome es el navegador desarrollado por Google.  En lo personal lo utilizo bastante aunque prefiero utilizar Firefox durante el desarrollo de aplicaciones web.

Desde su sitio web es posible descargar los paquetes DEB (Debian y Ubuntu) o RPM (Fedora y OpenSuse) para su instalación sin embargo he encontrado dificultades con algunas librerías que son dependencias del navegador.  Por este motivo es mas práctico utilizar directamente el repositorio que Google provee y cuyo procedimiento se describe a continuación.

Procedimiento de instalación.

Descargar e instalar la llave pública del repositorio de Google.

$ wget -q -O – https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -

Agregar el repositorio de Google a la lista de repositorios conocidos por el sistema operativo.

$ sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'

Actualizar el índice de paquetes de los repositorios.

$ sudo apt-get update

Instalar la versión estable de Google Chrome.

$ sudo apt-get install google-chrome-stable