QRcodes

Los códigos QR son una codificación abierta que permite almacenar información de forma visual para que pueda ser leída por receptores provistos de un dispositivo con cámara y software de descodificación. En la práctica, estos receptores son todos aquellos usuarios que cuentan con un smartphone con cámara y una app de descodificación de códigos QR, como puede ser Barcode Scanner en el caso de Android o i-nigma en el de iPhone.

Su uso es cada vez más extendido, tanto como recurso a la hora de taguear (mobile tagging) objetos o lugares del mundo físico con información digital, como hipervínculo visual a contenidos online en carteles o paneles de información.

El código que genera y descodifica imágenes QR es abierto, y existe una implementación open source escrita en Java que puede ser empleada en apps para Android. Ésta es la librería ZXing ("Zeta Cross-ing") y puede ser utilizada desde aplicaciones desarrolladas con Processing.

Intents y Activities en Android

Antes de continuar es necesario detenerse un momento en las figuras de los Intents y las Activities del sistema operativo Android. En Android todas las aplicaciones son Activities; esto es, una Activity es el proceso que ejecuta Android. Cada aplicación que escribimos en Processing se transforma en una Activity en el dispositivo Android. Lo interesante es que una Activity puede ser invocada desde dentro de otra. Esta llamada es lo que se conoce como Intent.

Así, los Intents son algo así como enlaces entre aplicaciones (Activities) Android. Podemos utilizar entonces desde nuestro proyecto en Processing otras aplicaciones que estén ya instaladas en nuestro dispositivo, y esto es muy interesante. Veamos cómo hacerlo en primer lugar en el caso que nos ocupa, la descodificación de códigos QR.

ZXing como Intent

El proyecto open source ZXing proporciona código Java para generar y descodificar QRcodes. Pero proporciona a su vez una aplicación de descodificación para Android, Barcode Scanner, que puede ser llamada y utilizada desde nuestra app en Processing.

Sean Owen, el desarrollador de ZXing, ha hecho tan buen trabajo para la comunidad con su software, que hasta proporciona un método que facilita la integración de la funcionalidad de su Barcode Scanner en cualquier desarrollo externo. Este método está integrado en las clases “IntentIntegrator” e “IntentResults”, que contienen todo lo necesario para lanzar la descodificación de un QR.

El primer paso para emplear su facilitador pasa por compilar -siguiendo estas instrucciones- el código que puede descargarse aquí. De todos los archivos producidos, el que nos permite continuar adelante es IntentIntegrator.jar, que se puede descargar directamente de aquí.

Para utilizar en Android Processing estas clases externas es necesario guardarlas en una carpeta llamada “code” que se encuentre en el directorio de nuestro sketch. A continuación, un código como el que sigue es suficiente para utilizarla:

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
 
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
 
String texto;
 
void setup() {
  size(screenWidth, screenHeight, OPENGL); // Importante lanzarlo como OPENGL... (¿pq?)
  texto = "Pulsa el botón para descodificar y espera un momento.";
}
 
 
void draw() {
  background(0);
  text(texto, 100, 100);
}
 
 
public boolean surfaceTouchEvent(MotionEvent me) {
  IntentIntegrator.initiateScan(this);
  return super.surfaceTouchEvent(me);
}
 
 
void onActivityResult(int requestCode, int resultCode, Intent intent) {
  if (requestCode == 195543262) {
    IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
    if (scanResult != null) {
      if (scanResult.getContents() != null) {
        texto = scanResult.getContents()+"\n"+texto;
      }
    }
  }
  super.onActivityResult(requestCode, resultCode, intent);
}

En primer lugar importamos dos conjuntos de recursos. Recursos de Android, por un lado, que nos permiten trabajar con Activities e Intents, y el facilitador, por el otro, del proyecto ZXing.

Este facilitador nos proporciona dos métodos. El primero,

IntentIntegrator.initiateScan(this);

cuando es invocado, lanza la aplicación decodificadora de QR a un primer plano, sin que el usuario abandone en ningún momento nuestra app. Barcode Scanner es entonces lanzada siempre que se encuentre instalada en el dispositivo del usuario. Está claro que esto no ocurrirá la mayor parte de las veces, y por ello IntentIntegrator, cuando no encuentra la aplicación descodificadora, se encarga de avisar al usuario y ofrecerle el enlace para su descarga directa.

Esta segunda funcionalidad exige que empleemos este método dentro de una función como surfaceTouchEvent(MotionEvent me). En el mouseReleased() de Processing, sin embargo, no funcionará.

El segundo método que incluye el helper de ZXing es

IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);

Esta es la fórmula encargada de traducir los resultados recibidos por el descodificador de ZXing. Esta es una fórmula que tiene que incluirse dentro de la función

void onActivityResult(int requestCode, int resultCode, Intent intent)

que es la encargada en Android de recibir automáticamente el resultado de la Activity invocada desde nuestro programa. Fijémonos en que esta función recoge los resultados con un “requestCode”, que es el que distingue unos de otros en el caso de que hayamos lanzado numerosas Activities. En el caso del facilitador de ZXing que entregamos compilado aquí, este requestCode es 195543262. Cuando lanzamos Activities directamente, sin helpers como el de ZXing, este número es uno que podremos especificar a nuestro antojo.

scanResult contiene entonces el resultado de la lectura de código. Sólo tenemos que recogerlo en una String y lo podemos emplear en nuestro Processing!

String resultados = scanResult.getContents();

Cómo generar un código QR

Codificar frases o URLs en una imagen QR es muy sencillo gracias a herramientas gratuitas en la red como ésta por ejemplo, que no requieren de más explicación.

Clase QRscanner

Podemos simplificar el desarrollo aún más mediante una clase como la siguiente:

import android.app.Activity;
import android.content.Intent;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
 
class QRscanner {
 
  PApplet parent;
 
  QRscanner(PApplet parent_) {
    parent = parent_;
  } 
 
 
 
  void start() {
    try {
      IntentIntegrator.initiateScan(parent);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
 
 
 
  String analiza(int requestCode, int resultCode, Intent intent) {
    String resultado = null;
 
    if (requestCode == 195543262) {
      IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
      if (scanResult != null) {
        if (scanResult.getContents() != null) {
          resultado = scanResult.getContents();
        }
      }
    }
 
    return resultado;
  }
}

Que pueda ser empleada de esta forma:

import android.app.Activity;
import android.content.Intent;
 
QRscanner qr;
 
String texto;
 
void setup() {
  size(screenWidth, screenHeight, OPENGL);
 
  texto = "Pulsa el botón para descodificar y espera un momento.";
  qr = new QRscanner(this);
}
 
 
void draw() {
  background(0);
  text(texto, 100, 100);
}
 
 
public boolean surfaceTouchEvent(MotionEvent me) {
  qr.start();
  return super.surfaceTouchEvent(me);
}
 
 
void onActivityResult(int requestCode, int resultCode, Intent intent) {
  String resultado = qr.analiza(requestCode, resultCode, intent);
 
  texto = resultado + "\n" + texto;
 
  super.onActivityResult(requestCode, resultCode, intent);  
}

Esto es:

// Declaración:
QRscanner qr;

// en setup:
qr = new QRscanner(this);

// para llamar al lector: 
qr.start(); // atención: no en mouseReleased(), sino en surfaceTouchEvent

// en onActivityResult:
String resultado = qr.analiza(requestCode, resultCode, intent);

Ejemplo con el código del integrator ZXing directamente en Processing

qrscanner_con_zxing_code.zip

import android.view.View;
 
void onResume() {
  View view = this.findViewById(android.R.id.content);
  view.bringToFront();
  super.onResume();
}