jueves, 23 de enero de 2014

Captura de imágenes con HTML5 y PhoneGap desde el móvil

Con el objetivo de seguir manteniendo vivo el blog, he creado este mini tutorial donde crearemos una página HTML5 con JavaScript que captura fotos gracias a PhoneGap.

Crear proyecto con phoneGap


Para crear el proyecto con PhoneGap os recomiendo repasar el post Primeros pasos con PhoneGap. Y como todo está muy claro en él, no me extenderé y pasaré directamente a daros los datos que necesitamos:

·      Proyecto: demoPic
·      Paquete: com.demo.pic

En mi caso he usado la versión de PhoneGap 2.9.1.

Una vez creado, recordar el importar el proyecto en el ADT (Eclipse) marcando la opción de copy projects into workspace.

Creación de la interfaz


Para este ejemplo, nuestra interfaz de usuario la haremos en HTML5 y será muy sencilla: un botón, una imagen y dos contenedores para mostrar información.

Con esto, crearemos el siguiente archivo index.html (sobre escribiremos el existente en assets/www):

<!DOCTYPE html>
<html>
  <head>
    <title>DemoPic</title>
  <body>
    <button onclick="capturePhoto();">Carpturar foto</button>
    <img style="display:none;width:100px;height:100px;" id="foto" src=""/>
    <div id="fichero"></div>
    <div id="info"></div>
  </body>
</html>

Como podéis apreciar, el documento tiene un botón que llama a una función capturePhoto, una imagen con dimensión 100x100 pixeles y dos contenedores llamados “fichero” e “info” para mostrar información de la ejecución de nuestro código JavaScript.

Importación de los scripts necesarios


PhoneGap nos ofrece una serie de scripts para utilizar en nuestras aplicaciones HTML5 que nos ayudan a manejar el teléfono: phonegap.js o cordova.js (dependiendo de la versión que usemos), el cual tendremos que
Importar en nuestra cabecera:

<!DOCTYPE html>
<html>
  <head>
    <title>DemoPic</title>
    <script type="text/javascript" charset="utf-8" src="cordova.js"></script>

Creando la función que captura las imágenes


Como habíamos comentado antes, nuestro botón invoca a una función para capturar la imagen: capturePhoto.

Esta función es tan simple que invoca a la API para captura de imágenes con la cámara del dispositivo (navigator.camera.getPicture) y le pasa como parámetros la función que javascript que se ejecuta si todo es correcto (onPhotoDataSuccess), la función que maneja el error (onFail) y un objeto con propiedades sobre la captura de la imagen (como por ejemplo la calidad).


<script type="text/javascript" charset="utf-8">
function capturePhoto() {
  navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: 50 });
}
</script>


Podéis encontrar más info sobre la función camera.getPicture de PhoneGap aquí: http://docs.phonegap.com/en/1.2.0/phonegap_camera_camera.md.html

En caso de fallo, nuestra función onFail simplemente nos mostrará un alert:

//control de error en la captura de fotos
function onFail(message) {
  alert('Fallo en la captura: ' + message);
}

En nuestra función de éxito (onPhotoDataSuccess) recibiremos el fichero que se ha generado al tomar la foto, por lo que solo tendremos que asignarlo a nuestra imagen.

//captura correcta
function onPhotoDataSuccess(imageData) {
  //obtenemos el objeto foto
  var foto = document.getElementById('foto');
  //lo mostramos
  foto.style.display = 'block';
  //asignamos la imagen
  foto.src = imageData;
  //mostramos lo que nos ha enviado
  document.getElementById('fichero').innerHTML = imageData;
}

Y listo, ya solo tenemos que ejecutarlo.



Convertir la imagen a Base64 y mostrarla.


Muchas veces, queremos capturar la imagen para almacenarla en alguna base de datos a simplemente enviarla. Para esto, lo más cómodo es hacer uso de Base64, y en nuestro caso, convertir la imagen a este formato es tan sencillo como leer el fichero.

En el parámetro imageData que recibimos en la función onPhotoDataSuccess recibimos el nombre (y ruta) del fichero de la imagen que se ha creado. Tendremos que crear una función que lea dicha imagen y nos devuelva el Byte-String que contiene el fichero.

function getBinary(file){
    var xhr = new XMLHttpRequest();
    xhr.open("GET", file, false);
    xhr.overrideMimeType("text/plain; charset=x-user-defined");
    xhr.send(null);
    return xhr.responseText;
}

Esta función la almacenaremos en un fichero a parte que llamaremos getBinary.js para poder usarla en otra ocasión.

Ahora que tenemos el contenido, tendremos que transformarlo en base 64, por lo que emplearemos la siguiente función:

function base64Encode(str) {
    var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var out = "", i = 0, len = str.length, c1, c2, c3;
    while (i < len) {
        c1 = str.charCodeAt(i++) & 0xff;
        if (i == len) {
            out += CHARS.charAt(c1 >> 2);
            out += CHARS.charAt((c1 & 0x3) << 4);
            out += "==";
            break;
        }
        c2 = str.charCodeAt(i++);
        if (i == len) {
            out += CHARS.charAt(c1 >> 2);
            out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
            out += CHARS.charAt((c2 & 0xF) << 2);
            out += "=";
            break;
        }
        c3 = str.charCodeAt(i++);
        out += CHARS.charAt(c1 >> 2);
        out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
        out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
        out += CHARS.charAt(c3 & 0x3F);
    }
    return out;
}

Esta función, al igual que la anterior, la almacenaremos en un fichero independiente llamado base64Encode.js.

Y ahora que tenemos ambas, simplemente tendremos que añadir su importación a nuestro fichero index.html.

<!DOCTYPE html>
<html>
  <head>
    <title>DemoPic</title>
    <script type="text/javascript" charset="utf-8" src="cordova.js"></script>
    <script type="text/javascript" charset="utf-8" src="js/base64Encode.js"></script>
    <script type="text/javascript" charset="utf-8" src="js/getBinary.js"></script>

Y tras esto, modificaremos el código de la función onPhotoDataSuccess para que lea el fichero de la imagen

var str = getBinary(imageData);

Convierta el contenido a base64:

var b64 = base64Encode(str);

y mostremos la imagen en base64:

foto.src = "data:image/jpeg;base64," + b64;

Por lo que nos quedará:

//captura correcta
function onPhotoDataSuccess(imageData) {
  //obtenemos el objeto foto
  var foto = document.getElementById('foto');
  //lo mostramos
  foto.style.display = 'block';
  //leemos el fichero de la imagen
  var str = getBinary(imageData);
  //convertimos el contenido a base 64
  var b64 = base64Encode(str);
  //asignamos la imagen en base64
  foto.src = "data:image/jpeg;base64," + b64;
  //mostramos lo que nos ha enviado la funcion
  document.getElementById('fichero').innerHTML = imageData;
  //mostramos el base64 de la imagen
  document.getElementById('info').innerHTML = b64;
}

Y a probar se ha dicho:





El código lo podéis encontrar aquí: ejemplo simple | ejemplo base64.