MvochoaMvochoa
Blog
Como crear una extensión para convertir imágenes en Base64 y viceversa | App Inventor

29 de junio de 2018

App Inventor, Base64

Como crear una extensión para convertir imágenes en Base64 y viceversa | App Inventor

Vamos a crear una extensión de app inventor para convertir la información de una imagen a base64 para poder manipularla como texto y también poder convertir una cadena en Base64 a una imagen.

¿En que caso seria util la extensión?


  • Para cuando tengamos la necesidad de almacenar una imagen dentro de una TinyDB.
  • Poder mandar una imagen dentro de un JSON a algún servidor.
  • También hay veces que tenemos una imagen en base64 y queremos colocarla en la herramienta Image. Como sabemos la herramienta Image no puede leer Base64.

Empecemos a construir la extensión


Primero que todo va ser necesario clonar el repositorio de App Inventor:

$ cd ~/
$ git clone https://github.com/mit-cml/appinventor-sources.git

Una vez clonado el repositorio, vamos a crear la ruta de nuestro paquete y el archivo java. El nombre del paquete no es muy relevante pero yo siempre uso el nombre de mi dominio claramente en forma de paquete.

$ cd ~/appinventor-sources/appinventor/components/src
$ mkdir -p com/mvochoa/base64
$ cd com/mvochoa/base64
$ touch Base64.java

Creamos la clase y agregamos la información de la extensión al archivo java Base64.java.

package com.mvochoa.base64;

import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.common.ComponentCategory;

@DesignerComponent(version = 1,
        description = "Codificador y Decodificar de imágenes en base64",
        category = ComponentCategory.EXTENSION,
        nonVisible = true,
        iconName = "images/extension.png")
public class Base64 {

}

También hay que especificar que es un objecto externo al igual que los permisos necesario para la extensión, va ser necesario poder escribir y leer de la memoria externa del dispositivo. Ademas hay que extender de la clase AndroidNonvisibleComponent ya que nuestra extensión no va ser un componente visible.

package com.mvochoa.base64;

import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;

@SimpleObject(external = true)
@UsesPermissions(permissionNames = "android.permission.WRITE_EXTERNAL_STORAGE, android.permission.READ_EXTERNAL_STORAGE")
public class Base64 extends AndroidNonvisibleComponent {

}

Creamos el constructor de la clase y obtenemos el Context que vamos a ocupar para poder obtener las rutas donde se almacena el Cache.

package com.mvochoa.base64;

import android.content.Context;

import com.google.appinventor.components.runtime.ComponentContainer;

public class Base64 extends AndroidNonvisibleComponent {
    Context context;

    public Base64(ComponentContainer container) {
        super(container.$form());
        context = (Context)container.$context();
    }
}

La primera función que vamos hacer es la de convertir una imagen a base64.

package com.mvochoa.base64;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.runtime.util.ErrorMessages;

import java.io.ByteArrayOutputStream;

public class Base64 extends AndroidNonvisibleComponent {
    @SimpleFunction(description = "Retorna una cadena en base64 de la imagen.")
    public String ImageToBase64(String path) {
        String base64 = "";
        try {
            Bitmap bm = BitmapFactory.decodeFile(path);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            byte[] byteArrayImage = baos.toByteArray();

            base64 = android.util.Base64.encodeToString(byteArrayImage, android.util.Base64.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
            form.dispatchErrorOccurredEvent(this, "ioBase64",
                    ErrorMessages.ERROR_CANVAS_BITMAP_ERROR, e.getMessage());
        }
        return base64;
    }
}

La función crea un Bitmap para poder obtener el arreglo de bytes de la imagen y obtenemos la cadena en Base64 con la información de la imagen usando la función android.util.Base64.encodeToString y se retorna la cadena obtenida.

La siguiente función es para convertir una cadena de texto en Base64 a una imagen y vamos a retornar la ruta de la imagen creada para eso ocupábamos los permisos, esto se hace para que la herramienta Image pueda leer la imagen.

package com.mvochoa.base64;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class Base64 extends AndroidNonvisibleComponent {
    @SimpleFunction(description = "Retorna la ruta de la imagen.")
    public String Base64ToImage(String base64) {
        String name = (System.currentTimeMillis() / 1000L) + "";
        try {
            File img = File.createTempFile(name, null, context.getExternalCacheDir());
            name = img.getAbsolutePath();

            FileOutputStream fos = new FileOutputStream(img);
            fos.write(android.util.Base64.decode(base64, android.util.Base64.DEFAULT));
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            form.dispatchErrorOccurredEvent(this, "ioBase64",
                    ErrorMessages.ERROR_CANNOT_SAVE_IMAGE, e.getMessage());
        }

        return name;
    }
}

Para el nombre de la imagen es de un numero de la fecha actual pero en formato Unix es para evitar duplicados y creamos un archivo temporal dentro de la carpeta de Cache, ya solo se escribe dentro del archivo la cadena en Base64 decodificada y listo tenemos nuestra imagen.

Como extra vamos agregar dos funciones adicionales para convertir un cadena de texto a Base64 y viceversa:

package com.mvochoa.base64;

public class Base64 extends AndroidNonvisibleComponent {
    @SimpleFunction(description = "Retorna la cadena codificada en base64.")
    public String TextToBase64(String text) {
        String base64 = "";
        try {
            base64 = android.util.Base64.encodeToString(text.getBytes(), android.util.Base64.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
            form.dispatchErrorOccurredEvent(this, "ioBase64", ErrorMessages.ERROR_CANVAS_BITMAP_ERROR, e.getMessage());
        }
        return base64;
    }

    @SimpleFunction(description = "Retorna la cadena en Base64 decodificada.")
    public String Base64ToText(String base64) {
        String text = "";
        try {
            text = new String(android.util.Base64.decode(base64, android.util.Base64.DEFAULT));
        } catch (Exception e) {
            e.printStackTrace();
            form.dispatchErrorOccurredEvent(this, "ioBase64", ErrorMessages.ERROR_CANVAS_BITMAP_ERROR, e.getMessage());
        }
        return text;
    }
}

Compilemos la extensión


Listo. Bueno ahora solo queda compilar la extensión para poderla usarla en app inventor. Ejecutamos los siguientes comandos:

$ cd ~/appinventor-sources/appinventor
$ ant clean
$ ant extensions

Listo con eso tenemos nuestro archivo .aix en la ruta ~/appinventor-sources/appinventor/components/build/extensions/com.mvochoa.base64.aix

El archivo com.mvochoa.base64.aix es el que tenemos que importar en app inventor.

¿Como se usa la extensión?


Entramos a http://ai2.appinventor.mit.edu/ y creamos un nuevo proyecto.

Crear proyecto

Una vez abierto el proyecto importáremos la extensión que es el archivo com.mvochoa.base64.aix.

Importar extensión

Ahora creamos un interfaz para probar la extensión con los siguientes componentes:

  • Image
    • Name: Image_Piker
    • Height: 30%
    • Width: Fill Parent
  • VerticalArrangement
    • Name: VerticalArrangement1
    • Height: 40%
    • Width: Fill Parent
    • Componentes:
      • ImagePiker
        • Name: BtnImagePiker
        • Width: Fill Parent
        • Text: Seleccionar Imagen
      • Label
        • Name: Label1
        • Width: Fill Parent
        • Text: Imagen en base64
        • FontBold: true
      • TextBox
        • Name: TxbBase64
        • Width: Fill Parent
        • Height: Fill Parent
        • MultiLine: true
        • Text:
      • Label
        • Name: Label2
        • Width: Fill Parent
        • Text: Base64 convertida a imagen
        • FontBold: true
  • Image
    • Name: Image_Base64
    • Height: 30%
  • Base64 | La extensión que acabamos de hacer.

Interfaz de la app para probar la extensión de Base64

Ahora en la parte de los bloques, vamos a usar el evento AfterPicking del componente BtnImagePiker para que se ejecute después de que se seleccione una imagen.

Coloquemos la imagen seleccionada en el atributo Picture del Image_Piker.

Usamos el método de ImageToBase64 de la extensión y en el atributo Path colocamos la ruta de la imagen seleccionada y la cadena devuelta la colocamos en atributo Text del componente TxbBase64.

Por ultimo usamos el método Base64ToImage de la extensión para convertir la cadena de Base64 a Imagen y colocarla en el atributo Picture de Image_Base64.

Bloques de la interfaz para probar la extensión Base64

Probamos la Aplicación


Gif del uso de la app para probar extensión Base64 Ya solo queda probar la aplicación este fue el resultado como puedes ver en le TextBox se escribe todo la información de la imagen en Base64 y esa misma se vuelve a leer y se muestra en Image.

Bueno eso seria todo te comparto los enlaces de los archivos:

Espero que te haya sido de ayuda. No olvides si ha sido util para ti seria de mucha ayuda si compartes este material con tus amigos.