En esta entrada veremos cómo crear un lector de códigos QR en Android en nuestras aplicaciones; para hacernos una idea más clara del funcionamiento de un lector QR, actualmente existen varios lectores QR en Android que los puedes encontrar en la Google Play; por nombrar algunos:
Desarrollando un lector QR en nuestra aplicación Android con la librería de Play Service
Como bien sabrás, Google ofrece una importante cantidad de funcionalidades que se traducen en várias API desarrolladas por el propio Google que pone a nuestra disposición de nosotros los desarrolladores mediante sus librerías así como la de Play Services así también como la de soportes entre muchas más; para hacer las labores más comunes como crear una aplicación para leer un código QR no es necesario emplear una librería o paquete de un tercero; nos basta con la que provee Google que hace todo el trabajo por nosotros.
Crear un lector QR personalizado (creado por nosotros mismos empleando como ambiente de desarrollo de software o IDE a Android Studio) es bastante sencillo con Android y solo debemos incluir una librería externa la cual corresponde a los servicios de Google Play: com.google.android.gms:play-services-vision
, pero antes de esto veamos qué es, cómo funciona un código QR y cómo podemos emplearlo en nuestras aplicaciones Android y webs:
¿Qué es un código QR?
Antes de entrar al tema, de cómo desarrollar un lector QR empleando una librería nativa provista por la gente de Google, es importante saber que es un lector de código QR y cómo funciona su estructura que es lo que explicaremos a continuación.
Un código QR no es más que un sistema que permite almacenar información en una especie de código de barras de última generación
Un código QR es un tipo de código de barra pero en dos dimensiones en el cual se codifica información en un cuadro como el siguiente
Los códigos QR son realmente útiles para administrar inventarios, compartir datos e información de una manera fácil y muy profesional; los códigos QR son actualmente un elemento fundamental en cualquier aplicación de pequeña o gran envergadura debido a su versatilidad al momento de usarlos.
¿Para qué sirve un Código QR? y como podemos explotarlos en nuestras apps Android
Como se comentó anteriormente, los códigos QR son ampliamente empleados debido a su versatilidad y gran utilidad; se emplean en todo tipo de productos como alimentos hasta llegar a dispositivos, automóviles, revistas y otros tantos de tipos de ámbitos publicidad, marketing, etc... y es debido a que con una app Android podemos escanear cualquier tipo de código QR.
...
Aunque nuestro caso de interés es mostrar mostrar el proceso de creación de una aplicación Android para emplearlo de scanner en una app Android...
...En donde podemos procesar medicinas e indicar al cliente si la medicina es de utilidad para su persona teniendo su perfil previamente cargado y sin necesidad de que el cliente suministre información o datos algunos de manera manual.
¿Cómo generar un código QR?
En Internet encontrarás una cantidad inmensa de formas para generar códigos QR; inclusive podemos crear códigos QR con librerías PHP, Java, etc; y existen webs que se encargan de generar códigos QR como la siguiente Genere su codigo QR gratis o alguno personalizado mediante algún lenguaje de programación como vimos anteriormente en: Generando códigos QR con CodeIgniter.
Scanner del QR: ¿Cómo descifrar un código QR?
Con la ayuda de un móvil podemos recuperar esta información tan solo con apuntar la cámara hacia el código QR y esa es la idea y lo que haremos en la siguiente sección de esta entrada en donde nos dedicaremos a detallar a como crear un lector QR con Android.
Desarrollando nuestro propio lector de códigos QR en Android
Finalmente llegamos al área de interés en cuál es crear nuestro lector de códigos QR en Android; primero debemos agregar la dependencia necesaria en nuestro build.gradle
.
Agregando la dependencia en el archivo build.gradle
en Android Studio
Abrimos nuestro archivo build.gradle
y agregamos la siguiente dependencia:
implementation 'com.google.android.gms:play-services:15.0.2'
La anterior dependencia nos da acceso no solo a la API de vision
si no a toda la plataforma que nos ofrece la Google Play como en nuestro caso de interés es solomenteleer un código QR, podemos emplear la siguiente dependencia en nuestro build.gradle
:
implementation 'com.google.android.gms:play-services-vision:15.0.2'
Debes tener presente que las versiones cambian de un día a otro; puedes encontrar la última versión en la documentación oficial en el siguiente enlace:
Para que el paquete actualice automáticamente podríamos hacer algo así implementation 'com.google.android.gms:play-services-vision:15.+'
Configurando el resto de la aplicación para nuestro lector QR
El archivo manifest para manejar los permisos
En nuestro manifest colocamos el uso de la cámara:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.desarrollolibre.qr.qr">
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
La vista de nuestro lector de códigos QR
Con nuestra dependencia ya configurada ahora podemos ir a nuestra actividad y/o fragment y crear los objetos, eventos y configuraciones necesarias para tener nuestro propio lector QR; pero primero debemos de configurar el layout del a actividad o fragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center_horizontal"
android:padding="8dp">
<SurfaceView
android:id="@+id/camera_view"
android:layout_width="640px"
android:layout_height="480px"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" />
</LinearLayout>
</LinearLayout>
Configurando el lector de código y la cámara en nuestra actividad
Y en nuestra actividad o fragment debemos crear un objeto de tipo BarcodeDetector
esta clase permite reconocer códigos de barra y códigos QR -el cual es nuestro caso de interés- a través de la cámara del dispositivo y devolver un objeto de tipo SparseArray
con el dato decodificado del QR analizado además de todo esto, creamos la fuente de la cámara y la resolución asociada:
// creo el detector qr
barcodeDetector =
new BarcodeDetector.Builder(getContext())
.setBarcodeFormats(Barcode.QR_CODE)
.build();
// creo la camara fuente
cameraSource = new CameraSource
.Builder(getContext(), barcodeDetector)
.setRequestedPreviewSize(640, 480)
.build();
El objeto CameraSource
permite obtener frames de la cámara del dispositivo y posteriormente analizarlo.
Especificamos el objeto SurfaceView
de nuestro layout; este objeto se encarga de espejar o reflejar lo que estamos viendo por la cámara del dispositivo en nuestra superficie de nuestro layout:
cameraView = (SurfaceView) v.findViewById(R.id.camera_view);
Ahora especificamos los listener o escuchadores que permiten controlar el ciclo de vida de la cámara:
// listener de ciclo de vida de la camara
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
// verifico si el usuario dio los permisos para la camara
if (ContextCompat.checkSelfPermission(getContext(), android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException ie) {
Log.e("CAMERA SOURCE", ie.getMessage());
}
} else {
Toast.makeText(getContext(), getResources().getString(R.string.error_camara), Toast.LENGTH_SHORT).show();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
Un punto importante del código anterior es iniciar la cámara una vez que nuestra superficie sea dibujada con cameraSource.start(cameraView.getHolder());.
Por último se prepara el evento que retornará el resultado provisto por la detección y procesamiento del QR; cuando existe una respuesta barcodes.size()
nos devolverá un valor mayor a cero:
// preparo el detector de QR
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
@Override
public void release() {
}
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes = detections.getDetectedItems();
if (barcodes.size() != 0) {
barcodes.valueAt(0).displayValue.toString();
// hacer algo
}
barcodeDetector.release();
});
Con el barcodes.valueAt(0).displayValue.toString();
obtenemos el dato devuelto por el QR que puede ser un texto, entero o una URL dependiendo cómo configuremos nuestro código QR.
Los permisos en Android 6 y superior
A partir de Android 6 Google creó un nuevo esquemas de permisos los cuales son solicitados al usuarios al momento de requerir emplear alguna función de dicho(s) permiso(s)...
...por lo tanto no basta con solo incluir la dependencia en nuestro AndroidManifest
, también debemos solicitar el permiso propiamente dicho desde el código java de nuestra aplicación Android.
Entonces, al momento de construir nuestra superficie colocamos el siguiente código:
// listener de ciclo de vida de la camara
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
// verifico si el usuario dio los permisos para la camara
if (ActivityCompat.checkSelfPermission(QRActiviy.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// verificamos la version de Android que sea al menos la M para mostrar
// el dialog de la solicitud de la camara
if (shouldShowRequestPermissionRationale(
Manifest.permission.CAMERA)) ;
requestPermissions(new String[]{Manifest.permission.CAMERA},
MY_PERMISSIONS_REQUEST_CAMERA);
}
return;
} else {
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException ie) {
Log.e("CAMERA SOURCE", ie.getMessage());
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
El código es relativamente sencillo, verificamos si el permiso fue concedido con checkSelfPermission
y con shouldShowRequestPermissionRationale
mostramos el dialog para solicitar el permiso.
Finalmente el código queda de la siguiente manera:
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.webkit.URLUtil;
import com.google.android.gms.vision.CameraSource;
import com.google.android.gms.vision.Detector;
import com.google.android.gms.vision.barcode.Barcode;
import com.google.android.gms.vision.barcode.BarcodeDetector;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
private CameraSource cameraSource;
private SurfaceView cameraView;
private final int MY_PERMISSIONS_REQUEST_CAMERA = 1;
private String token = "";
private String tokenanterior = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cameraView = (SurfaceView) findViewById(R.id.camera_view);
initQR();
}
public void initQR() {
// creo el detector qr
BarcodeDetector barcodeDetector =
new BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build();
// creo la camara
cameraSource = new CameraSource
.Builder(this, barcodeDetector)
.setRequestedPreviewSize(1600, 1024)
.setAutoFocusEnabled(true) //you should add this feature
.build();
// listener de ciclo de vida de la camara
cameraView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
// verifico si el usuario dio los permisos para la camara
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// verificamos la version de ANdroid que sea al menos la M para mostrar
// el dialog de la solicitud de la camara
if (shouldShowRequestPermissionRationale(
Manifest.permission.CAMERA)) ;
requestPermissions(new String[]{Manifest.permission.CAMERA},
MY_PERMISSIONS_REQUEST_CAMERA);
}
return;
} else {
try {
cameraSource.start(cameraView.getHolder());
} catch (IOException ie) {
Log.e("CAMERA SOURCE", ie.getMessage());
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
// preparo el detector de QR
barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
@Override
public void release() {
}
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes = detections.getDetectedItems();
if (barcodes.size() > 0) {
// obtenemos el token
token = barcodes.valueAt(0).displayValue.toString();
// verificamos que el token anterior no se igual al actual
// esto es util para evitar multiples llamadas empleando el mismo token
if (!token.equals(tokenanterior)) {
// guardamos el ultimo token proceado
tokenanterior = token;
Log.i("token", token);
if (URLUtil.isValidUrl(token)) {
// si es una URL valida abre el navegador
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(token));
startActivity(browserIntent);
} else {
// comparte en otras apps
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, token);
shareIntent.setType("text/plain");
startActivity(shareIntent);
}
new Thread(new Runnable() {
public void run() {
try {
synchronized (this) {
wait(5000);
// limpiamos el token
tokenanterior = "";
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
Log.e("Error", "Waiting didnt work!!");
e.printStackTrace();
}
}
}).start();
}
}
}
});
}
}
Lector de códigos QR para Kotlin
Como bien sabrás si eres lector de este blog, ya tenemos un tiempo dando los primeros pasos con Kotlin y como no podría faltar, a continuación mostramos toda la implementación anterior pero en vez de Java sería para Kotlin todo nuestro código quedaría de la siguiente manera:
private var cameraSource: CameraSource? = NULL
private var cameraView: SurfaceView? = NULL
private val MY_PERMISSIONS_REQUEST_CAMERA = 1
private var token = ""
private var tokenanterior = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
cameraView = findViewById<View>(R.id.camera_view)
initQR()
}
fun initQR() {
// creo el detector qr
val barcodeDetector = BarcodeDetector.Builder(this)
.setBarcodeFormats(Barcode.ALL_FORMATS)
.build()
// creo la camara
cameraSource = CameraSource.Builder(this, barcodeDetector)
.setRequestedPreviewSize(1600, 1024)
.setAutoFocusEnabled(true) //you should add this feature
.build()
// listener de ciclo de vida de la camara
cameraView!!.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
// verifico si el usuario dio los permisos para la camara
if (ActivityCompat.checkSelfPermission(this@MainActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// verificamos la version de ANdroid que sea al menos la M para mostrar
// el dialog de la solicitud de la camara
if (shouldShowRequestPermissionRationale(
Manifest.permission.CAMERA))
;
requestPermissions(arrayOf(Manifest.permission.CAMERA),
MY_PERMISSIONS_REQUEST_CAMERA)
}
return
} else {
try {
cameraSource!!.start(cameraView!!.holder)
} catch (ie: IOException) {
Log.e("CAMERA SOURCE", ie.message)
}
}
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {
cameraSource!!.stop()
}
})
// preparo el detector de QR
barcodeDetector.setProcessor(object : Detector.Processor<Barcode>() {
fun release() {}
fun receiveDetections(detections: Detector.Detections<Barcode>) {
val barcodes = detections.getDetectedItems()
if (barcodes.size() > 0) {
// obtenemos el token
token = barcodes.valueAt(0).displayValue.toString()
// verificamos que el token anterior no se igual al actual
// esto es util para evitar multiples llamadas empleando el mismo token
if (token != tokenanterior) {
// guardamos el ultimo token proceado
tokenanterior = token
Log.i("token", token)
if (URLUtil.isValidUrl(token)) {
// si es una URL valida abre el navegador
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(token))
startActivity(browserIntent)
} else {
// comparte en otras apps
val shareIntent = Intent()
shareIntent.action = Intent.ACTION_SEND
shareIntent.putExtra(Intent.EXTRA_TEXT, token)
shareIntent.type = "text/plain"
startActivity(shareIntent)
}
Thread(object : Runnable {
override fun run() {
try {
synchronized(this) {
wait(5000)
// limpiamos el token
tokenanterior = ""
}
} catch (e: InterruptedException) {
// TODO Auto-generated catch block
Log.e("Error", "Waiting didnt work!!")
e.printStackTrace()
}
}
}).start()
}
}
}
})
}
En el código anterior puede notar que al momento de detectar una lectura del código QR por el móvil se realiza una serie de pasos que va desde la obtención del mismo mediante barcodes.valueAt(0).displayValue.toString();
, la validación (y liberación con el Thread
) para evitar leer el mismo QR de forma consecutiva (el lector QR siempre sigue ejecutándose independientemente de si ha procesado un QR o no) y luego mediante URLUtil.isValidUrl(token)
verificamos si es una URL el token obtenido, y en ese caso habre en navegador predefinido de nuestro teléfono, en caso contrario simplemente mostramos el dialog para compartir contenido por las distintas aplicaciones que tengamos en nuestro teléfono.
Al correr la aplicación, obtendremos una pantalla como la siguiente:
Cabe recordar que una vez que obtenemos el código podemos hacer lo que queramos, generalmente si estamos creando una aplicación personalizada (Ve como crear códigos QR en CodeIgniter), es probable que queramos enviarlo a nuestra aplicación para validarlo o simplemente mostrarlo al usuario mediante un dialog o abrir un navegador como especificamos anteriormente.
Puedes emplear el siguiente QR que devuelve la URL de este Blog:
Un punto importante es que puedes emplear la misma funcionalidad para leer los barcode o código de barra en Android, se emplea el mismo core de la app y de esta manera puedes crear una app bastante potente que sirva como reader o lector de códigos QR mediante la implementación de la librería y la cámara del dispositivo.
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter