Créer une application de suivi du poids avec Cloud Firestore

Stocker les données de vos applications dans le nuage est très important de nos jours, car les utilisateurs ont tendance à posséder plusieurs appareils et souhaitent que leurs applications soient synchronisées entre eux. Avec Cloud Firestore, une base de données NoSQL en temps réel disponible sur la plate-forme Firebase, cette tâche est plus simple et sécurisée que jamais.

Dans un précédent tutoriel, je vous ai présenté toutes les fonctionnalités puissantes offertes par Cloud Firestore. Aujourd'hui, je vais vous montrer comment l'utiliser avec d'autres produits Firebase, tels que FirebaseUI Auth et Firebase Analytics, pour créer une application de suivi du poids simple et hautement évolutive..

Conditions préalables

Pour suivre ce didacticiel pas à pas, vous aurez besoin des éléments suivants:

  • la dernière version d'Android Studio
  • un compte Firebase
  • et un appareil ou un émulateur sous Android 5.0 ou supérieur

1. Configuration du projet

Pour pouvoir utiliser les produits Firebase dans votre projet Android Studio, vous aurez besoin du plug-in Gradient de Google Services, d'un fichier de configuration de Firebase et de la mise en oeuvre dépendances. Avec l'Assistant Firebase, vous pouvez tous les obtenir très facilement.

Ouvrez l'assistant en allant à Outils> Firebase. Ensuite, sélectionnez le Analytique option et cliquez sur le Enregistrer un événement d'analyse lien.

Vous pouvez maintenant appuyer sur le Se connecter à Firebase bouton pour connecter votre projet Android Studio à un nouveau projet Firebase.

Cependant, pour ajouter réellement le plugin et le la mise en oeuvre dépendances, vous devrez également appuyer sur la Ajouter des analyses à votre application bouton.

L'application de suivi du poids que nous créons aujourd'hui ne comportera que deux fonctions: stocker des poids et les afficher sous forme de liste triée par ordre chronologique inverse. Nous allons bien sûr utiliser Firestore pour stocker les poids. Pour les afficher sous forme de liste, nous utiliserons toutefois les composants liés à Firestore disponibles dans la bibliothèque FirebaseUI. Par conséquent, ajoutez ce qui suit la mise en oeuvre dépendance à la app modules build.gradle fichier:

implementation 'com.firebaseui: firebase-ui-firestore: 3.2.2'

Les utilisateurs doivent pouvoir afficher uniquement leurs propres poids, pas ceux de tous ceux qui utilisent l'application. Par conséquent, notre application doit avoir la capacité d'identifier de manière unique ses utilisateurs. FirebaseUI Auth offre cette possibilité, ajoutez donc la dépendance suivante:

implémentation 'com.firebaseui: firebase-ui-auth: 3.2.2'

Nous aurons également besoin de quelques widgets Material Design pour donner à notre application un aspect agréable. Assurez-vous donc que vous ajoutez les bibliothèques Design Support et Material Dialogs comme dépendances..

implémentation 'com.android.support:design:26.1.0' implémentation 'com.afollestad.material-dialogs: noyau: 0.9.6.0'

Enfin, appuyez sur le Synchroniser maintenant bouton pour mettre à jour le projet.

2. Configuration de l'authentification Firebase

L'authentification Firebase prend en charge un grand nombre de fournisseurs d'identité. Cependant, tous sont désactivés par défaut. Pour en activer un ou plusieurs, vous devez visiter la console Firebase..

Dans la console, sélectionnez le projet Firebase que vous avez créé à l’étape précédente, accédez à son Authentification section et appuyez sur la Configurer la méthode de connexion bouton.

Pour permettre aux utilisateurs de se connecter à notre application en utilisant un compte Google, activez Google en tant que fournisseur, donnez un nom significatif au projet, puis appuyez sur le bouton sauvegarder bouton.

Google est le fournisseur d'identité le plus simple que vous puissiez utiliser. Il n'a pas besoin de configuration et votre projet Android Studio n'aura pas besoin de dépendances supplémentaires..

3. Configuration de Cloud Firestore

Vous devez activer Firestore dans la console Firebase avant de commencer à l'utiliser. Pour ce faire, allez au Base de données section et appuyez sur la Commencer bouton présent dans le Bêta de Firestore Cloud carte.

Vous serez maintenant invité à sélectionner un mode de sécurité pour la base de données. Assurez-vous de choisir le Démarrer en mode verrouillé option et appuyez sur le Activer bouton.

En mode verrouillé, par défaut, personne ne pourra accéder ni modifier le contenu de la base de données. Par conséquent, vous devez maintenant créer une règle de sécurité permettant aux utilisateurs de lire et d'écrire uniquement les documents qui leur appartiennent. Commencez par ouvrir le Règles languette.

Avant de créer une règle de sécurité pour notre base de données, nous devons préciser comment nous allons y stocker les données. Alors disons que nous allons avoir une collection de niveau supérieur nommée utilisateurs contenant des documents qui représentent nos utilisateurs. Les documents peuvent avoir des ID uniques identiques aux ID générés par le service d'authentification Firebase pour les utilisateurs..

Étant donné que les utilisateurs ajouteront plusieurs entrées de poids à leurs documents, il est idéal d’utiliser un sous-ensemble pour stocker ces entrées. Appelons la sous-collection poids.

Sur la base du schéma ci-dessus, nous pouvons maintenant créer une règle pour le chemin utilisateurs / utilisateur_id / poids / poids. La règle sera qu'un utilisateur est autorisé à lire et à écrire sur le chemin uniquement si le identifiant d'utilisateur la variable est égale à l'ID d'authentification Firebase de l'utilisateur.

En conséquence, mettez à jour le contenu de l'éditeur de règles.

service cloud.firestore correspondance / bases de données / base de données / documents correspondance / utilisateurs / utilisateur_id / poids / poids autoriser la lecture, écrire: si utilisateur_id == request.auth.uid; 

Enfin, appuyez sur le Publier bouton pour activer la règle.

4. Authentification des utilisateurs

Notre application ne doit être utilisable que si l'utilisateur est connecté à l'aide d'un compte Google. Par conséquent, dès son ouverture, il doit vérifier si l'utilisateur dispose d'un ID d'authentification Firebase valide. Si l'utilisateur a l'identifiant, il doit continuer et afficher l'interface utilisateur. Sinon, il devrait afficher un écran de connexion.

Pour vérifier si l’utilisateur a un identifiant, il suffit de vérifier que le utilisateur actuel propriété du FirebaseAuth la classe n'est pas nulle. Si elle est nulle, nous pouvons créer une intention de connexion en appelant le createSignInIntentBuilder () méthode du AuthUI classe.

Le code suivant vous montre comment procéder pour Google en tant que fournisseur d'identité:

if (FirebaseAuth.getInstance (). currentUser == null) // Connectez-vous startActivityForResult (AuthUI.getInstance (). createSignInIntentBuilder () .setAvailableProviders (arrayListOf (.AutofIg.BlishBuilder)) ()) ), 1) else // Déjà connecté à showUI ()

Notez que nous appelons une méthode nommée showUI () si un identifiant valide est déjà présent. Cette méthode n'existe pas encore, alors créez-la et laissez son corps vide pour l'instant.

showUI fun privé () // à faire

Pour saisir le résultat de l’intention de connexion, nous devons remplacer la onActivityResult () méthode de l'activité. Dans la méthode, si la valeur de resultCode l'argument est RESULT_OK et le utilisateur actuel Si la propriété n'est plus nulle, cela signifie que l'utilisateur a réussi à se connecter. Dans ce cas, nous devons à nouveau appeler le showUI () méthode pour rendre l'interface utilisateur.

Si l'utilisateur ne parvient pas à se connecter, nous pouvons afficher un toast et fermer l'application en appelant le terminer() méthode.

En conséquence, ajoutez le code suivant à l'activité:

écrasez 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) // Connexion réussie à showUI () else // Echec de la connexion à Toast.makeText (this, "Vous devez vous connecter pour continuer", Toast.LENGTH_LONG) .show () finish () 

À ce stade, si vous exécutez l'application pour la première fois, vous devriez pouvoir voir un écran de connexion qui ressemble à ceci:

Lors des exécutions suivantes, grâce à Google Smart Lock, activé par défaut, vous serez automatiquement connecté..

5. Définir les mises en page

Notre application nécessite deux mises en page: une pour l'activité principale et une pour les entrées de poids qui seront affichées en tant qu'éléments de la liste chronologique inverse.

L’agencement de l’activité principale doit avoir une RecyclerVoir widget, qui agira comme la liste, et un FloatingActionButton widget, sur lequel l'utilisateur peut appuyer pour créer une nouvelle entrée de poids. Après les avoir placés tous les deux dans un Disposition relative Le fichier XML de présentation de votre activité doit ressembler à ceci:

     

Nous avons associé un gestionnaire d'événements sur un clic nommé addWeight () avec le FloatingActionButton widget. Le gestionnaire n'existe pas encore, créez-lui un talon dans l'activité..

fun addWeight (v: View) // À faire

Pour que la disposition de l’entrée du poids soit simple, nous n’aurons que deux Affichage widgets à l'intérieur: un pour afficher le poids et l'autre pour afficher l'heure à laquelle l'entrée a été créée. Utilisant un LinearLayout widget en tant que conteneur pour eux suffira.

En conséquence, créez un nouveau fichier XML de mise en page nommé weight_entry.xml et ajoutez-y le code suivant:

    

6. Créer un modèle

A l'étape précédente, vous avez constaté que chaque poids est associé à une durée et à une durée. Pour informer Firestore à ce sujet, nous devons créer un modèle pour la saisie du poids..

Les modèles Firestore sont généralement des classes de données simples avec les variables de membre requises.

Classe de données WeightEntry (poids var: Double = 0.0, horodatage var: Long = 0)

C'est également le bon moment pour créer un détenteur de vue pour chaque entrée de poids. Comme vous l'avez peut-être deviné, le détenteur de la vue sera utilisé par le RecyclerVoir widget pour rendre les éléments de la liste. Alors créez une nouvelle classe nommée PoidsEntréeVH, qui étend la RecyclerView.ViewHolder classe, et créer des variables de membre pour les deux Affichage widgets. N'oubliez pas de les initialiser en utilisant le findViewById () méthode. Le code suivant vous montre comment faire de manière concise:

class 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. Création de documents utilisateur uniques

Lorsqu'un utilisateur essaie de créer une entrée de poids pour la première fois, notre application doit créer un document séparé pour l'utilisateur à l'intérieur du utilisateurs collection sur Firestore. Comme nous l'avons décidé précédemment, l'ID du document ne sera rien d'autre que l'ID d'authentification Firebase de l'utilisateur, qui peut être obtenu à l'aide de la commande uid propriété du utilisateur actuel objet.

Pour obtenir une référence à la utilisateurs collection, nous devons utiliser le collection() méthode du FirebaseFirestore classe. Nous pouvons alors appeler son document() méthode et passer le uid comme argument pour créer le document de l'utilisateur.

Nous aurons besoin d'accéder aux documents spécifiques à l'utilisateur tout en lisant et en créant les entrées de poids. Pour éviter de coder deux fois la logique ci-dessus, je vous suggère de créer une méthode distincte..

amusement privé getUserDocument (): DocumentReference val db = FirebaseFirestore.getInstance () val users = db.collection ("users") val uid = FirebaseAuth.getInstance (). currentUser !!. uid renvoie users.document (uid)

Notez que le document sera créé une seule fois par utilisateur. En d'autres termes, plusieurs appels à la méthode ci-dessus renverront toujours le même document, tant que l'utilisateur utilise le même compte Google..

8. Ajouter des entrées de poids

Lorsque les utilisateurs appuient sur le bouton d'action flottante de notre application, ils doivent pouvoir créer de nouvelles entrées de poids. Pour leur permettre de taper leurs poids, créons maintenant une boîte de dialogue contenant un Éditer le texte widget. Avec la bibliothèque Material Dialog, le faire est extrêmement intuitif.

À l'intérieur de addWeight () méthode, qui sert de gestionnaire d’événements sur le clic du bouton, crée un MaterialDialog.Builder instance et appeler son Titre() et contenu() méthodes pour donner à votre dialogue un titre et un message significatif. De même, appelez le type d'entrée() méthode et passe TYPE_CLASS_NUMBER comme argument pour s'assurer que l'utilisateur ne peut taper que des chiffres dans la boîte de dialogue.

Ensuite, appelez le contribution() méthode pour spécifier un indice et associer un gestionnaire d’événements à la boîte de dialogue. Le gestionnaire recevra le poids que l'utilisateur a saisi en tant qu'argument.

Enfin, assurez-vous d'appeler le spectacle() méthode pour afficher le dialogue.

MaterialDialog.Builder (this) .title ("Ajouter un poids") .content ("Quel est votre poids aujourd'hui?") .InputType (InputType.TYPE_CLASS_NUMBER ou InputType.TYPE_NUMBER_FLAG_DECIMAL) .input ("poids en livres", ",", false, _, weight -> // To do) .show ()

Dans le gestionnaire d'événements, nous devons maintenant ajouter du code pour créer et remplir un nouveau document de saisie de poids. Parce que le document doit appartenir à la poids collection du document unique de l'utilisateur, pour accéder à la collection, vous devez appeler le collection() méthode du document qui est retourné par le getUserDocument () méthode.

Une fois que vous avez la collection, vous pouvez appeler sa ajouter() méthode et passer une nouvelle instance du PoidsEntrée la classe pour stocker l'entrée.

getUserDocument () .collection ("pondérations") .add (WeightEntry (weight.toString (). toDouble (), Date (). heure))

Dans le code ci-dessus, vous pouvez voir que nous utilisons le temps propriété du Rendez-vous amoureux classe pour associer un horodatage à l'entrée.

Si vous exécutez l'application maintenant, vous devriez pouvoir ajouter de nouvelles entrées de poids dans Firestore. Vous ne les verrez pas dans l'application pour l'instant, mais ils seront visibles dans la console Firebase..

9. Affichage des entrées de poids

Il est maintenant temps de peupler le RecyclerVoir widget de notre mise en page. Alors commencez par créer une référence pour elle en utilisant le findViewById () méthode et attribution d'une nouvelle instance du LinearLayoutManager classe à elle. Cela doit être fait à l'intérieur du showUI () méthode que nous avons créée plus tôt.

val weightsView = findViewById(R.id.weights) weightsView.layoutManager = LinearLayoutManager (this)

le RecyclerVoir Le widget doit afficher tous les documents présents dans le poids collection du document de l'utilisateur. De plus, les derniers documents devraient apparaître en premier. Pour répondre à ces exigences, il faut maintenant créer une requête en appelant le collection() et commandé par() les méthodes.

Par souci d’efficacité, vous pouvez limiter le nombre de valeurs renvoyées par la requête en appelant le limite() méthode.

Le code suivant crée une requête qui renvoie les 90 dernières entrées de pondération créées par l'utilisateur:

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

En utilisant la requête, nous devons maintenant créer un FirestoreRecyclerOptions objet, que nous utiliserons plus tard pour configurer l’adaptateur de notre RecyclerVoir widget. Quand tu passes le question par exemple au setQuery () méthode de son générateur, assurez-vous de spécifier que les résultats renvoyés sont sous la forme de PoidsEntrée objets. Le code suivant vous montre comment procéder:

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

Vous avez peut-être remarqué que nous faisons de notre activité actuelle le propriétaire du cycle de vie du FirestoreRecyclerOptions objet. Cette opération est importante car nous souhaitons que notre adaptateur réponde correctement aux événements de cycle de vie courants, tels que l'ouverture ou la fermeture de l'application par l'utilisateur..

À ce stade, nous pouvons créer un FirestoreRecyclerAdapter objet, qui utilise le FirestoreRecyclerOptions objet pour se configurer. Parce que le FirestoreRecyclerAdapter classe est abstraite, Android Studio doit automatiquement remplacer ses méthodes pour générer un code qui ressemble à ceci:

val adaptateur = objet: FirestoreRecyclerAdapter(options) substitue fun onBindViewHolder (titulaire: WeightEntryVH, position: Int, modèle: WeightEntry) // À faire substitue fun surCreateViewHolder (parent: ViewGroup ?, viewType: Int): WeightEntryVH // À faire

Comme vous pouvez le voir, le FirestoreRecyclerAdapter la classe est très similaire à la RecyclerView.Adapter classe. En fait, il en dérive. Cela signifie que vous pouvez l'utiliser de la même manière que vous utiliseriez le RecyclerView.Adapter classe.

À l'intérieur de onCreateViewHolder () méthode, tout ce que vous avez à faire est de gonfler la weight_entry.xml fichier de mise en page et renvoyer un PoidsEntréeVH voir titulaire objet basé sur elle.

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

Et à l'intérieur du onBindViewHolder () méthode, vous devez utiliser le modèle argument pour mettre à jour le contenu de la Affichage widgets présents dans le détenteur de la vue.

En mettant à jour le poids voir le widget est simple, mettre à jour le timeView le widget est légèrement compliqué car nous ne voulons pas montrer directement l'horodatage, qui est en millisecondes, à l'utilisateur.

Le moyen le plus simple de convertir l’horodatage en date et heure lisibles est d’utiliser le formatDateTime () méthode du DateUtils classe. En plus de l'horodatage, la méthode peut accepter plusieurs indicateurs différents, qu'elle utilisera pour formater la date et l'heure. Vous êtes libre d'utiliser des drapeaux qui correspondent à vos préférences..

// Afficher le poids holder.weightView? .Text = "$ model.weight lb" // Afficher la date et l'heure val formatedDate = DateUtils.formatDateTime (applicationContext, model.timestamp, DateUtils.FORMAT_SHOW_DATE ou DateUtils.FORMAT_SHOW_TIH. ) holder.timeView? .text = "Le $ formatéDate"

Enfin, n'oubliez pas de pointer le RecyclerVoir widget à l'adaptateur que nous venons de créer.

weightsView.adapter = adaptateur

L'application est prête. Vous devriez maintenant pouvoir ajouter de nouvelles entrées et les voir apparaître dans la liste presque immédiatement. Si vous exécutez l'application sur un autre appareil disposant du même compte Google, les mêmes poids y apparaissent également..

Conclusion

Dans ce didacticiel, vous avez constaté à quel point il était facile et rapide de créer une application de suivi du poids entièrement fonctionnelle pour Android à l'aide de Cloud Firestore en tant que base de données. N'hésitez pas à ajouter plus de fonctionnalités à cela! Je vous suggère également d'essayer de le publier sur Google Play. Avec le plan Firebase Spark, qui offre actuellement 1 Go de stockage de données gratuit, vous n'aurez aucun problème à servir au moins quelques milliers d'utilisateurs..

Et pendant que vous êtes ici, consultez certains de nos autres articles sur le développement d'applications Android.!