Crear una aplicación de seguimiento de peso con Cloud Firestore

Almacenar los datos de su aplicación en la nube es muy importante en estos días porque los usuarios tienden a poseer múltiples dispositivos y desean que sus aplicaciones estén sincronizadas en todos ellos. Con Cloud Firestore, una base de datos NoSQL en tiempo real disponible en la plataforma Firebase, hacerlo es más fácil y más seguro que nunca..

En un tutorial anterior, le presenté todas las características poderosas que Cloud Firestore tiene para ofrecer. Hoy, le mostraré cómo usarlo junto con otros productos Firebase, como FirebaseUI Auth y Firebase Analytics, para crear una aplicación de seguimiento de peso simple, pero muy escalable..

Prerrequisitos

Para seguir este tutorial paso a paso, necesitarás:

  • La última versión de Android Studio
  • una cuenta Firebase
  • y un dispositivo o emulador con Android 5.0 o superior

1. Configuración del proyecto

Para poder utilizar los productos Firebase en su proyecto de Android Studio, necesitará el complemento Gradle de Servicios de Google, un archivo de configuración de Firebase y algunos implementación dependencias Con el Asistente de Firebase, puede obtenerlos todos muy fácilmente..

Abre el asistente yendo a Herramientas> Firebase. A continuación, seleccione la Analítica opción y haga clic en el Registrar un evento de análisis enlazar.

Ahora puede presionar el Conectarse a Firebase Botón para conectar su proyecto de Android Studio a un nuevo proyecto de Firebase.

Sin embargo, para agregar realmente el plugin y el implementación dependencias, necesitarás también presionar el Añade Analytics a tu aplicación botón.

La aplicación de seguimiento de peso que estamos creando hoy tendrá solo dos características: almacenar pesos y mostrarlos como una lista ordenada en orden cronológico inverso. Por supuesto, usaremos Firestore para almacenar los pesos. Sin embargo, para mostrarlos como una lista, usaremos los componentes relacionados con Firestore disponibles en la biblioteca de FirebaseUI. Por lo tanto, agregue lo siguiente implementación dependencia a la aplicación módulo de construir.gradle expediente:

implementación 'com.firebaseui: firebase-ui-firestore: 3.2.2'

Los usuarios deben poder ver solo sus propios pesos, no los pesos de todos los que usan la aplicación. Por lo tanto, nuestra aplicación debe tener la capacidad de identificar de forma única a sus usuarios. FirebaseUI Auth ofrece esta capacidad, así que agregue la siguiente dependencia a continuación:

implementación 'com.firebaseui: firebase-ui-auth: 3.2.2'

También necesitaremos algunos widgets de Material Design para dar a nuestra aplicación un aspecto agradable. Así que asegúrese de agregar la biblioteca de Soporte de diseño y la biblioteca de Diálogos de materiales como dependencias.

implementación 'com.android.support:design:26.1.0' implementación 'com.afollestad.material-dialogs: core: 0.9.6.0'

Finalmente, pulse la tecla Sincronizar ahora botón para actualizar el proyecto.

2. Configuración de la autenticación Firebase

La autenticación Firebase admite una variedad de proveedores de identidad. Sin embargo, todos ellos están deshabilitados por defecto. Para habilitar uno o más de ellos, debes visitar la consola Firebase.

En la consola, seleccione el proyecto Firebase que creó en el paso anterior, vaya a su Autenticación y presione la tecla Configurar el método de inicio de sesión botón.

Para permitir que los usuarios inicien sesión en nuestra aplicación utilizando una cuenta de Google, habilite Google como proveedor, dé un nombre público significativo al proyecto y presione la tecla Salvar botón.

Google es el proveedor de identidad más fácil que puedes usar. No necesita configuración, y su proyecto de Android Studio no necesitará dependencias adicionales para ello.

3. Configurando Cloud Firestore

Debe habilitar Firestore en la consola de Firebase antes de comenzar a usarlo. Para ello, ve a la Base de datos sección y presione el Empezar botón presente en el Cloud Firestore Beta tarjeta.

Ahora se le pedirá que seleccione un modo de seguridad para la base de datos. Asegúrate de elegir el Iniciar en modo bloqueado opción y presione el Habilitar botón.

En el modo bloqueado, de forma predeterminada, nadie podrá acceder ni modificar el contenido de la base de datos. Por lo tanto, ahora debe crear una regla de seguridad que permita a los usuarios leer y escribir solo aquellos documentos que les pertenecen. Comience abriendo el Reglas lengüeta.

Antes de crear una regla de seguridad para nuestra base de datos, debemos finalizar cómo almacenaremos los datos en ella. Así que digamos que vamos a tener una colección de alto nivel llamada usuarios Contiene documentos que representan a nuestros usuarios. Los documentos pueden tener identificadores únicos que son idénticos a los identificadores que el servicio Firebase Authentication genera para los usuarios.

Debido a que los usuarios agregarán varias entradas de peso a sus documentos, lo ideal es utilizar una subcolección para almacenar esas entradas. Llamemos a la sub-colección pesos.

En función del esquema anterior, ahora podemos crear una regla para la ruta usuarios / id_usuario / pesos / peso. La regla será que a un usuario se le permite leer y escribir en la ruta solo si el id_usuario variable es igual al ID de autenticación Firebase del usuario.

En consecuencia, actualice los contenidos del editor de reglas..

service cloud.firestore match / database / database / documents match / users / user_id / weight / weight permitir lectura, escribir: if user_id == request.auth.uid; 

Finalmente, pulse la tecla Publicar botón para activar la regla.

4. Autentificando Usuarios

Nuestra aplicación debe ser utilizable solo si el usuario ha iniciado sesión con una cuenta de Google. Por lo tanto, tan pronto como se abre, debe verificar si el usuario tiene una identificación válida de Firebase Authentication. Si el usuario tiene la ID, debe seguir adelante y renderizar la interfaz de usuario. De lo contrario, debería mostrar una pantalla de inicio de sesión..

Para verificar si el usuario tiene una identificación, podemos simplemente verificar que el usuario actual propiedad de la FirebaseAuth La clase no es nula. Si es nulo, podemos crear un intento de inicio de sesión llamando al createSignInIntentBuilder () método de la Autenti clase.

El siguiente código le muestra cómo hacerlo para Google como proveedor de identidad:

if (FirebaseAuth.getInstance (). currentUser == null) // Iniciar sesión startActivityForResult (AuthUI.getInstance (). createSignInIntentBuilder () .setAvailableProviders). ), 1) else // Ya ha iniciado sesión en showUI ()

Tenga en cuenta que estamos llamando a un método llamado showUI () si una identificación válida ya está presente. Este método aún no existe, así que créalo y deje su cuerpo vacío por ahora..

diversión privada showUI () // hacer

Para capturar el resultado del intento de inicio de sesión, debemos anular el onActivityResult () Método de la actividad. Dentro del método, si el valor de la código de resultado argumento es RESULTADO_OK y el usuario actual la propiedad ya no es nula, significa que el usuario logró iniciar sesión correctamente. En este caso, debemos llamar de nuevo al showUI () Método para renderizar la interfaz de usuario..

Si el usuario no puede iniciar sesión, podemos mostrar un brindis y cerrar la aplicación llamando al terminar() método.

En consecuencia, agregue el siguiente código a la actividad:

override fun onActivityResult (requestCode: Int, resultCode: Int, data: Intent?) super.onActivityResult (requestCode, resultCode, data) if (requestCode == 1) if (resultCode == Activity.RESULT_OK && FirebaseAuth.getInstance () .currentUser! = null) // Se ha iniciado sesión correctamente en showUI () else // No se pudo iniciar sesión Toast.makeText (esto, "Debes iniciar sesión para continuar", Toast.LENGTH_LONG) .show () finish () 

En este punto, si ejecuta la aplicación por primera vez, debería poder ver una pantalla de inicio de sesión que se ve así:

En las siguientes ejecuciones, gracias a Google Smart Lock, que está habilitada de forma predeterminada, iniciará sesión automáticamente..

5. Definiendo Diseños

Nuestra aplicación necesita dos diseños: uno para la actividad principal y otro para las entradas de peso que se mostrarán como elementos de la lista de orden cronológico inverso.

El trazado de la actividad principal debe tener un RecyclerView widget, que actuará como la lista, y un Botón de acción flotante Widget, que el usuario puede presionar para crear una nueva entrada de peso. Después de colocarlos ambos dentro de un Disposición relativa Widget, el archivo XML de diseño de su actividad debería verse así:

     

Hemos asociado un controlador de eventos on-click llamado addWeight () con el Botón de acción flotante widget El manejador aún no existe, así que cree un código auxiliar para él dentro de la actividad.

fun addWeight (v: View) // To do

Para mantener el diseño de la entrada de peso simple, vamos a tener sólo dos Vista de texto widgets dentro de él: uno para mostrar el peso y el otro para mostrar el momento en que se creó la entrada. Usando un LinearLayout widget como un contenedor para ellos será suficiente.

En consecuencia, cree un nuevo archivo XML de diseño llamado weight_entry.xml y añádele el siguiente código:

    

6. Creando un modelo

En el paso anterior, vio que cada entrada de peso tiene un peso y un tiempo asociados. Para que Firestore sepa sobre esto, debemos crear un modelo para la entrada de peso.

Los modelos de Firestore suelen ser clases de datos simples con las variables de miembro requeridas.

clase de datos WeightEntry (peso var: Double = 0.0, marca de tiempo var: Long = 0)

Ahora también es un buen momento para crear un titular de vista para cada entrada de peso. El titular de la vista, como habrás adivinado, será utilizado por el RecyclerView Widget para renderizar los elementos de la lista. Así que crea una nueva clase llamada WeightEntryVH, que extiende el RecyclerView.ViewHolder clase, y crear variables miembro tanto para el Vista de texto widgets No olvides inicializarlos usando el findViewById () método. El siguiente código le muestra cómo hacerlo de manera concisa:

clase WeightEntryVH (itemView: View?): RecyclerView.ViewHolder (itemView) var weightView: TextView? = itemView? .findViewById (R.id.weight_view) var timeView: TextView? = itemView? .findViewById (R.id.time_view)

7. Creación de documentos de usuario únicos

Cuando un usuario intenta crear una entrada de peso por primera vez, nuestra aplicación debe crear un documento separado para el usuario dentro de la usuarios Colección en Firestore. Como decidimos anteriormente, el ID del documento no será más que el ID de Autentificación Firebase del usuario, que se puede obtener utilizando el uid propiedad de la usuario actual objeto.

Para obtener una referencia a la usuarios colección, debemos utilizar el colección() método de la FirebaseFirestore clase. Entonces podemos llamar a su documento() método y pasar el uid como argumento para crear el documento del usuario.

Tendremos que acceder a los documentos específicos del usuario al leer y crear las entradas de peso. Para evitar codificar la lógica anterior dos veces, le sugiero que cree un método separado para ello..

private fun getUserDocument (): DocumentReference val db = FirebaseFirestore.getInstance () val users = db.collection ("users") val uid = FirebaseAuth.getInstance (). currentUser !!. uid return users.document (uid)

Tenga en cuenta que el documento se creará solo una vez por usuario. En otras palabras, las llamadas múltiples al método anterior siempre devolverán el mismo documento, siempre que el usuario use la misma cuenta de Google.

8. Agregar entradas de peso

Cuando los usuarios presionan el botón de acción flotante de nuestra aplicación, deben poder crear nuevas entradas de peso. Para permitirles escribir sus pesos, creamos ahora un diálogo que contiene un Editar texto widget Con la biblioteca Material Dialog, hacerlo es extremadamente intuitivo..

Dentro de addWeight () método, que sirve como el controlador de eventos de clic en el botón, crea un MaterialDialog.Builder instancia y llamar a su título() y contenido() Métodos para darle a su diálogo un título y un mensaje significativo. Del mismo modo, llame al tipo de entrada() método y pase TYPE_CLASS_NUMBER como argumento para asegurarse de que el usuario solo puede escribir números en el cuadro de diálogo.

A continuación, llame al entrada() Método para especificar una sugerencia y asociar un controlador de eventos con el cuadro de diálogo. El manejador recibirá el peso que el usuario escribió como argumento..

Por último, asegúrese de llamar al espectáculo() método para mostrar el diálogo.

MaterialDialog.Builder (this) .title ("Add Weight") .content ("¿Cuál es tu peso hoy?") .InputType (InputType.TYPE_CLASS_NUMBER o InputType.TYPE_NUMBER_FLAG_DECIMAL) .input ("peso en libras", "", falso, _, peso -> // Para hacer) .show ()

Dentro del controlador de eventos, ahora debemos agregar código para crear y completar un nuevo documento de entrada de peso. Porque el documento debe pertenecer a la pesos colección del documento único del usuario, para acceder a la colección, debe llamar al colección() Método del documento que es devuelto por el getUserDocument () método.

Una vez que tengas la colección, puedes llamar a su añadir() Método y pasar una nueva instancia de la WeightEntry clase para almacenar la entrada.

getUserDocument () .collection ("weight") .add (WeightEntry (weight.toString (). toDouble (), Date (). time))

En el código anterior, puede ver que estamos usando el hora propiedad de la Fecha clase para asociar una marca de tiempo con la entrada.

Si ejecuta la aplicación ahora, debería poder agregar nuevas entradas de peso a Firestore. Todavía no los verá en la aplicación, pero estarán visibles en la consola Firebase..

9. Visualización de las entradas de peso

Ahora es el momento de poblar el RecyclerView Widget de nuestro diseño. Así que empieza por crear una referencia para él usando el findViewById () Método y asignando una nueva instancia de la LinearLayoutManager clase para ello. Esto debe hacerse dentro del showUI () método que creamos anteriormente.

val pesosView = findViewById(R.id.weights) pesosView.layoutManager = LinearLayoutManager (esto)

los RecyclerView El widget debe mostrar todos los documentos que están presentes dentro de la pesos Colección del documento del usuario. Además, los últimos documentos deben aparecer primero. Para cumplir con estos requisitos, ahora debemos crear una consulta llamando al colección() y orderBy () metodos.

En aras de la eficiencia, puede limitar el número de valores devueltos por la consulta llamando al límite() método.

El siguiente código crea una consulta que devuelve las últimas 90 entradas de peso creadas por el usuario:

val query = getUserDocument (). collection ("pesos") .orderBy ("timestamp", Query.Direction.DESCENDING) .limit (90)

Usando la consulta, ahora debemos crear un FirestoreRecyclerOptions objeto, que utilizaremos más adelante para configurar el adaptador de nuestro RecyclerView widget Cuando pasas el consulta instancia a la setQuery () método de su constructor, asegúrese de especificar que los resultados devueltos están en la forma de WeightEntry objetos. El siguiente código le muestra cómo hacerlo:

val options = FirestoreRecyclerOptions.Builder() .setQuery (consulta, WeightEntry :: class.java) .setLifecycleOwner (this) .build ()

Es posible que haya notado que estamos haciendo de nuestra actividad actual el propietario del ciclo de vida de FirestoreRecyclerOptions objeto. Hacer esto es importante porque queremos que nuestro adaptador responda adecuadamente a los eventos comunes del ciclo de vida, como el usuario que abre o cierra la aplicación..

En este punto podemos crear una FirestoreRecyclerAdapter objeto, que utiliza el FirestoreRecyclerOptions Objeto para configurarse a sí mismo. Porque el FirestoreRecyclerAdapter la clase es abstracta, Android Studio debería anular automáticamente sus métodos para generar código que se parece a esto:

adaptador de val = objeto: FirestoreRecyclerAdapter(opciones) anular la diversión enBindViewHolder (titular: WeightEntryVH, posición: Int, modelo: WeightEntry) // To do anular la diversión enCreateViewHolder (padre: ViewGroup ?, viewType: Int): WeightEntryVH // To do

Como puedes ver, la FirestoreRecyclerAdapter clase es muy similar a la RecyclerView.Adapter clase. De hecho, se deriva de ello. Eso significa que puede usarlo de la misma manera que usaría el RecyclerView.Adapter clase.

Dentro de onCreateViewHolder () Método, todo lo que necesitas hacer es inflar el weight_entry.xml archivo de diseño y devolver un WeightEntryVH ver el objeto titular basado en él.

val layout = layoutInflater.inflate (R.layout.weight_entry, null) devuelve WeightEntryVH (layout)

Y dentro de la onBindViewHolder () método, debes usar el modelo argumento para actualizar los contenidos de la Vista de texto Widgets que están presentes dentro del titular de la vista..

Mientras se actualiza el pesoVer El widget es sencillo, actualizando el vista del tiempo El widget es un poco complicado porque no queremos mostrar la marca de tiempo, que está en milisegundos, al usuario directamente.

La forma más fácil de convertir la marca de tiempo en una fecha y hora legibles es utilizar el formatDateTime () método de la DateUtils clase. Además de la marca de tiempo, el método puede aceptar varios indicadores diferentes, que utilizará para formatear la fecha y la hora. Eres libre de usar banderas que coincidan con tus preferencias.

// Show weight holder.weightView? .Text = "$ model.weight lb" // Muestra la fecha y la hora val formattedDate = DateUtils.formatDateTime (applicationContext, model.timestamp, DateUtils.FORMAT_Fotivation) o DateUtils.FORMAT_SHOW_TIME o DateUtils.F.jpg ) holder.timeView? .text = "On $ formattedDate"

Finalmente, no te olvides de señalar RecyclerView Widget para el adaptador que acabamos de crear..

pesosView.adapter = adaptador

La aplicación está lista. Ahora debería poder agregar nuevas entradas y verlas aparecer en la lista casi de inmediato. Si ejecuta la aplicación en otro dispositivo que tenga la misma cuenta de Google, verá que las mismas entradas de peso también aparecen en ella.

Conclusión

En este tutorial, vio lo rápido y fácil que es crear una aplicación de seguimiento de peso totalmente funcional para Android utilizando Cloud Firestore como base de datos. ¡Siéntase libre de agregarle más funcionalidad! También te sugiero que intentes publicarlo en Google Play. Con el plan Firebase Spark, que actualmente ofrece 1 GB de almacenamiento de datos de forma gratuita, no tendrá problemas para atender al menos a unos pocos miles de usuarios.

Y mientras estás aquí, echa un vistazo a algunas de nuestras otras publicaciones sobre el desarrollo de aplicaciones para Android!