An open API service indexing awesome lists of open source software.

https://github.com/l-applin/acclimate

Public acclimate repository for reference
https://github.com/l-applin/acclimate

Last synced: 2 months ago
JSON representation

Public acclimate repository for reference

Awesome Lists containing this project

README

        

# Rapport final

## INTRODUCTION
Le projet consiste à créer une application Android permettant d’informer les utilisateurs sur différentes alertes environnementales. Plus précisément, l’objectif fixé en début de projet était de concevoir et d’intégrer toutes les composantes nécessaires au fonctionnement de l’application, incluant un serveur pour la communication avec les appareils clients, une base de donnée pour conserver les informations, une application Android permettant d’interagir avec le système ainsi qu’une application web permettant de consulter les informations. Autrement dit, nous voulions nous donner une véritable expérience de développement logiciel digne d'une entreprise professionnelle et produire une application utile pour la communauté, permettant notamment de sensibiliser les gens aux changements climatiques.
Plusieurs défis ont été rencontrés tout au long du projet par tous les membres de l’équipe. Personnellement, puisque mes tâches étaient principalement reliées à l’application mobile, plusieurs des défis rencontrés ont été en lien avec le fonctionnement de l’API spécifique au développement d’application mobile Android. Aussi, ce projet m’aura permis de mieux comprendre certaines idées de programmation orienté objet en continuant de développer ma connaissance du langage Java. Finalement, puisque mes tâches spécifiques comportaient étant entre autres la gestion des communications de l’application mobile avec le serveur, le projet fut une première expérience de développement d’une architecture client pour un serveur RESTFULL.
Une première ébauche de l’application a été créée durant la fin de semaine du 4, 5 et 6 mai 2018 à l’occasion du l’évènement « HackQC 2018 - Open Data Hackathon Données Ouvertes du Québec ». Cette ébauche, bien que fonctionnelle, comportaient malheureusement plusieurs problèmes majeurs, tel des modules très couplés, des algorithmes bêtement inefficaces et le non-respect de plusieurs principes de développement pour Android. Afin d’avoir une application fonctionnelle, robuste et maintenable à la fin du projet, une réécriture presque complète s’imposait. Cette ébauche aura toutefois permis d’effectuer un premier contact avec plusieurs des technologies utilisées et de se familiariser avec les interfaces qu’elles offrent.

## OSMDROID (CARTE)
Dès le départ, le choix d’utiliser Open Street Map comme engin pour l’affichage de la carte a été fait, plutôt que d’utiliser le géant GoogleMap. L’idée de travailler avec une technologie open source était intéressante, en plus d’être moins dépendant le l’empire Google. Toutefois, puisque l’environnement Firebase a aussi été intégré dans le projet (une autre technologie de Google), l’application est tout de même dépendante des fonctionnalités offertes par les produits Google. Plusieurs librairies sont disponibles pour travailler avec Open Street Map sur Android, et la plus intéressante semblait être OSMdroid. Elle est utilisé par plusieurs autres projet et offre des fonctionnalités qui nous semblaient intéressantes pour Acclimate, tel l’affichage de polygone facilité, un control complet sur le design des objets ajoutés à la carte (marqueurs, polygones, etc.) et l’accès à la cache des tuiles de la carte. Effectivement, plusieurs de ces fonctionnalités ont été intégrées au projet, tel l’affichage de marqueurs avec leur bulle d’information personnalisée qui s’affiche lorsque l’utilisateur appui sur le marqueur, le déplacement automatique de la carte en réponse à certain évènements, l’ajout de réponse au touché et au touché maintenu (long pressed) et l’ajout de la position courante du téléphone.
OSMdroid est une belle technologie qui fonctionne assez bien. Toutefois, son principal problème est son manque de documentation. Bien que les options de base offertes aux programmeurs soient bien expliquées dans des guides, dès que le désir d’intégrer une fonctionnalité plus complexe ou spécifique vient en tête la faible couverture de la documentation devient rapidement un véritable problème. Un des défis a donc été de comprendre le fonctionnement de la librairie malgré la documentation parfois déficiente. Un objet important de la librairie est le MapView. C’est le point d’entré pour l’affichage de l’affichage de la carte dans l’environnement Android. Une instance de cette classe offre plusieurs fonctionnalités de base assez bien documentée (mais assez facilement compréhensible par elle-même) tel que getBoundingBox, getMapCenter, getLatitudeSpan, getLatitudeSpan, getZoomLevel, etc. Toutefois les opérations plus complexes s’effectuent grâce à des Classes différentes, dont `MapView` possède des instances. On pense par exemple aux classes Projection, MapController et MapOverlay qui sont comprises dans MapView. Certaines de ces classes possèdent une au moins une documentation minimale (Projection, par exemple explique bien les comment les méthodes fonctionnent) alors que d’autres (`MapController`) n’est tout simplement pas documentée et les guides offerts ne couvrent pas toutes les méthodes. Par exemple, il n’a pas été trop difficile de centrer la carte sur les marqueurs. Cet opération ce fait avec le MapController qu’il est possible de récupérer avec Mapview.getController() et sa méthode animateTo(final IGeoPoint point, final Double pZoom, final Long pSpeed). Toutefois, comment savoir que pSpeed est la vitesse en millisecondes de l’animation ? Il aura fallu creuser loin dans le code source pour le comprendre.

Travailler avec une carte apporte son lot de défis spécifiques mais très intéressants. Par exemple, la méthode animateTo mentionnée plus haut est utilisée pour centrer la carte non pas directement sur le marqueur, mais sur l’info bulle qui s’ouvre lorsque l’utilisateur touche le marqueur. Il faut donc calculer un écart vertical pour trouver le nouveau point géographique exact sur lequel centrer la carte, mais cet écart doit tenir compte du zoom. Cet écart doit aussi être calculé en fonction de la dimension de l’écran du téléphone, puisque les téléphones ont tous des dimensions différentes. Il faut donc convertir le point géographique en cordonnée de pixel sur l’écran. Évidemment, nul part dans la documentation de OSMdroid il n’est mentionné que la classe Projection permet de faire cet opération avec la méthode `toPixel()` et `fromPixel()`.

## REQUETES HTTP
Une composante névralgique de l’application est le module des requêtes http envoyées au serveur puisque c’est la manière dont l’information contenue sur la base de donnée est récupérée. C’est donc une fonctionnalité qui est au cœur de toute les opérations et qui se doit d’être le plus robuste possible. Une décision a donc été prise : faire un compromis entre la flexibilité et la robustesse de ces requêtes. Puisque le serveur supporte un nombre requête qui n’est pas trop élevé (environs un quinzaine) et que chacune de ces requêtes ne varie que très peu dans sa structure, limiter la manière dont ces requête sont construites et envoyées au serveur dans le code permet d’offrir une structure beaucoup plus robuste. Effectivement, les causes des erreurs seront minimisées et, lors du débogage, cela permet de se concentrer non pas sur la construction de la requête, mais sur les autres éléments qui pourraient avoir causé l’erreur. Pour se faire, un module de requête spécifique pour le serveur Acclimate a été créé dans l’application Android afin d’abstraire le processus d’instanciation. Ceci permet de spécifier uniquement les paramètres unique à la requête, souvent sous forme d’objet propre à l’application, sans se soucier de la représentation interne de la requête elle même. C’est alors que l’idée d’utiliser une structure s’inspirant des patrons de conception Builer et Factory semble être intéressante. Au lieu d’instancier directement les objets HttpRequest, des classes auxiliaires correspondants aux différentes méthodes de requêtes (GET, POST, PATCH, DELETE) sont créées, avec des méthodes qui retournent une instance de la classe HttpRequest construite adéquatement. Pour faciliter la tâche d’avantage et maintenir une structure de code simple, maintenable et facilement modifiable, des classes privées qui extends HttpRequest (une classe abstract) sont utilisé à l’intérieur de chacune des classes correspondantes à une requête.

Ainsi, il ne suffit que d’utiliser les méthodes static des classes auxiliaires GetRequest, PostRequest, PatchRequest ou DeleteRequest afin de récupérer une instance de la classe HttpRequest, sans avoir eu à se soucier des nombreuses classes à gérer lors de la création d’une requête (`RestTemplate`, `HttpHeaders`, `HttpEntity`, `ResponseEntity`, `RequestErrorException`, etc).
Cette strucure est construire par dessus la libraire Spring For Android, un framework permettant de « faciliter » l’envoi de requête http. Le choix de cette librairie a été fait car le serveur utilise déjà Spring MVC pour la réception des requêtes et l’interaction avec la base de donnée. L’idée était d’utiliser deux framework compatibles sur le serveur et sur le téléphone. Toutefois, ce fut une mauvaise idée. La libraire Spring For Android est trop récente et elle souffre du même problème que OSMdroid, un manque de documentation. Il existe des guides, bien sur, mais qui ne couvre pas toute les fonctionnalités ou comment effectuer certaines opérations. Bien sur, Spring For Android offre une très grande flexibilité pour l’envoi de requête, et un autre de ses avantages est une abstraction sur le type désiré de retour des requêtes. Autrement dit, il est possible d’automatiquement convertir la réponse du serveur en un objet Java utilisé par l’application. C’est un gros avantage, mais en fin de compte, pour effectuer la gestion des messages d’erreurs de retour du serveur, il a été nécessaire de passer par une représentation intermédiaire en String et de éventuellement convertir en objet en utilisant la librairie Jackson (la même que Spring for Android utilise, de toute manière). Si le projet était à refaire, une autre libraire de gestion des requêtes serait considérée et le module des requêtes pourrait alors être grandement simplifié. L’avantage d’avoir créé le module des requêtes en gardant en tête un couplage minime et une forte cohésion, est qu’il ne rend public que certaines méthodes pour construire et envoyer les requêtes et recevoir la réponse. Son fonctionnement interne est complètement caché du reste du code. Il ne serait pas trop difficile alors de modifier l’implémentation avec une autre librairie que Spring for Android en conservant les mêmes interfaces publiques.
Un autre défi en lien avec les requêtes fut la gestion des erreurs renvoyées par le serveur. Plusieurs causes peuvent être à la source d’une erreur lors d’un échange entre l’application et le serveur : mauvaise requête (par exemple, pas le bon url), problème du coté serveur, erreur lors de la conversion de la réponse en objet Java, etc. De plus, la détection de la réception d’une erreur doit parfois être utilisée pour modifier le comportement d’une opération et diriger vers l’une ou l’autre prochaine étape différente dépendamment du succès ou de l’échec de la requête. Un problème fut aussi la façon dont le serveur gère ces erreurs. Le code http 200 est utilisé pour une requête ayant réussi. Toutefois, il était possible que le serveur renvoi un code 200 même lorsque la requête a échoué ! Un travail a été fait pour tenter de ne jamais renvoyer 200 lorsque la requête n’est pas complété, mais prendre pour acquis que tout les cas sont gérés ne permet pas d’avoir une infrastructure assez robuste. De plus, le message d’erreur renvoyé peut avoir une structure différente. Encore une fois, un effort a été mis pour rendre les messages d’erreurs uniformes, mais on ne peut pas prendre pour acquis que tout les cas sont on été pris en compte. Tous ces problèmes mènent à une gestion des erreurs dans le module des requêtes très difficile. Le code est difficile à comprendre et ne permet pas de bien distinguer toutes ces différentes raisons qui pourraient causer une erreur. Plutôt, une attention particulière a été mise sur les cas où la gestion de l’erreur est utilisée pour décider qu’elle opération effectuer. Par exemple, il arrive dans l’application de devoir aller chercher l’utilisateur sur le serveur. Si cet utilisateur est inexistant, il faut en créer un nouveau. L’importance de détecter cette erreur issue du serveur est donc capitale, puisque la création de nouveaux utilisateurs en dépend. Pour l’instant la gestion des erreurs est construite autours de ces cas critiques, avec une gestion générale plutôt vague. Ce serait à modifier afin d’avoir un gestion d’erreur globale spécifique et robuste qui ne dépend pas de cas spécifique afin d’avoir une application plus facilement maintenable. Pour ce faire, il faudrait trouver une solution pour s’assurer que le comportement du serveur soit, à 100%, conforme à une spécification quelconque que l’application pourrait prendre pour acquis. Une autre solution possible serait d’éviter d’utiliser la détection d’une erreur lors de la requête pour contrôler les opérations à faire dans l’application.
Évidemment, ce module doit être testé à fond afin de s’assurer de sa résilience aux problèmes si l’idée est d’éliminer les erreurs causées par la construction de la requête. JUnit semble alors être la solution. Toutefois, les tests dans Android sont plus compliqués, et tester les requêtes aura été un casse-tête. Android oblige tout travail qui requiert une connexion internet à être effectué ailleurs que dans le fil d’exécution (thread) principal de l’affichage, sinon, la redoutable NetworkOnMainThreadException est lancée. Le problème : les tests JUnit ne sont pas effectués sur de fil d’exécution principal de l’affichage et ne permettent donc pas de vérifier exactement le comportement de la requête. Puisque ces requêtes sont au cœur du fonctionnement de l’application mobile, une batterie de test pouvant être lancé sur le fil d’exécution principal de l’affichage au lancement de l’application a été mise en place. Évidemment, ces tests ne seraient pas inclus dans une version finale. Ils permettent toutefois de vérifier non seulement le bon fonctionnement des requêtes, mais aussi que le comportement asynchrone des tâches effectuées sur un nouveau fil d’exécution est conforme à ce qui est attendu.
Ce comportement asynchrone des requêtes a été un autre défi à relever. Bien qu’abordé dans le cours IFT 1025 - Programmation 2, la programmation asynchrone fut une nouvelle réalité avec laquelle il a été difficile de s’adapter au départ. Android offre la classe `AsyncTask` afin de faciliter ce genre d’opérations. En utilisant les méthodes fournies `doInBackground(Params...)` et `onPostExecute(Result)`, l’application est alors en mesure d’effectuer la requête sans utiliser le fil d’exécution principal de l’affichage et de pouvoir appeler des mise à jour de l’affichage sur ce fil une fois la requête complétée. Tout ce processus est abstrait dans une classe RequestHandler qui gère la requête à envoyer en utilsant l’héritage de `AsyncTask` et l’idée des méthodes callback. La gestion d’erreur peut alors être utilisé efficacement puisque le RequestHandler est en mesure de détecter une erreur lors de l’envoi de la requête et appeler la méthode `run()` d’une des deux interfaces `HttpErrorCallback` ou `HttpResponseCallback`.

## ALERTES
Un des objectifs principal du projet est d’afficher les alertes environnementales officielles issues de différentes sources et de permettre aux usagers de placer de nouvelles alertes. Le design des marqueurs était donc à faire. Il est important de garder en tête que les usagers qui utilisent logiciel puissent clairement différencier les alertes confirmée provenant d’une source officielle, de celle de sources non-officielles (particulièrement, autres usagers) qui pourraient ne pas être valable. C’est un des problèmes à régler : comment ne pas alarmer les usagers avec des alertes qui pourraient ne pas être réelles ? Une des solutions est d’utiliser un design différent pour les deux catégories d’alerte :

Une fois le design effectué (par noter collaborateur Charles-Philip Lepage), l’affichage des ces marqueurs sur la carte n’est pas trop compliqué. OSMdroid fourni une classe Marker, dont la classe créé Pin hérite, ce qui permet de les rajouter directement sur la carte avec `mapDisply.getOverlays.add(Marker m)`. Il existe donc dans le code une relation très forte entre les alertes et les marqueurs affichés. Se sont toutefois deux entités différentes qui doivent tout de même rester indépendante le plus possible les uns des autres. L’architecture des modules et des classe a été faites de manière à limiter ces interactions à celles qui sont uniquement nécéssaires au bon fonctionnement de l’application. Plus de détails sont donnés dans la section Android et architecture de l'application.
Afin de fournir des informations supplémentaires, deux affichages différents ont été créé : l’info-bulle qui apparaît lors du touché du marqueur qui affiche des informations réduite et une section plein écran qui affiche toute les informations de l’alerte. La conception des ses affichages fut un peu plus compliqué puisqu’il aura fallu très bien comprendre le fonctionnement des Layouts, l’unité de base de l’affichage pour Android.
Une attention particulière a été donné au rafraichissement des alertes en mémoire sur le téléphone afin de limiter la consommation de la batterie et aussi de minimiser les donné consommées par l’application. Effectuer plusieurs requêtes au serveur peut être une opération couteuse qui diminue la qualité de l’application. Une première requête au serveur est envoyée à l’ouverture de l’application pour recueillir les alertes en cours de sources officielles ainsi que les alertes publiées par les usagers. Ces alertes sont alors sauvegardées sur l’appareil de l’utilisateur avec la date et l’heure précise actuelle. Lors de la prochaine mise à jour des alertes requise, l’application vérifie cette signature temporelle et si elle est à l’intérieur d’une fenêtre de temps définie par l’usager, l’application n’envoi pas une nouvelle requête au serveur mais utilise plutôt les alertes sauvegardées sur l’appareil. Une conséquence importante à considérer est que les alertes affichées pourraient alors ne pas être mise à jour. C’est un compromis à faire entre donnée mobile et fraicheur des alertes qui est laissé entre les mains de l’utilisateur dans les préférences de l’application.
Afin de pouvoir consulter des alertes émises par d’autres usagers, il faut bien sur offrir la possibilité à ces usagers de pouvoir publier des nouvelles alertes ! Ceci s’effectue dans un environnement spécifique de l’application (Activité) sous-forme de formulaire. Cette fonctionnalité aura été l’occasion de comprendre comment travailler avec les différentes structures nécessaires pour construire un formulaire, tel que les différents TextEdit pour récupérer un champ textuel, le Spinner pour offrir une sélection à l’usager et l’implantation d’observateur sur les modifications des champs afin de pouvoir mettre à jour l’affichage en réponse aux modifications. Ceci permet à l’usager de vérifier en temps réel la version finale de l’info-bulle qui sera affiché en lien avec son alerte.

Plusieurs détails ont du être pris en compte lors des différentes opérations en lien avec les alertes pour s’assurer d’avoir un affichage cohérent avec les informations stockées serveurs. Par exemple, lors de l’ajout ou du retrait d’une alerte par un usager, il est important de mettre à jour l’affichage (afin de donner un retour à l’utilisateur sur son action), mais rétablir l’ancien affichage si l’opération échoue, pour cause d’erreur du serveur ou autre. En cas de succès, il aura aussi fallu mettre à jour les informations autant en mémoire dans le programme que sauvegardé sur le téléphone.
Une autre fonctionalité à implémenter fut la possibilité pour les usagers de prendre des photos pour documenter les alertes. Puisque ces fichiers sont trop volumineux pour la base de donnée SQL utilisé pour le projet, la solution utilisée pour l’instant est Firebase Cloud Storage. Le projet a donc été aussi une opportunité de travailler avec les outils professionnels de développement de logiciel offert par Google. Le téléversement des images peut alors directement être effectué à partir du téléphone. Ainsi, le serveur n’a donc qu’à conserver le lien vers l’image correspondant à la photo, et l’application sur le téléphonne s’occupe d’aller directement télécharger l’image qui correspond à ce lien à partir du stockage de Firebase. Toutefois, cette opération pourrait facilement devenir couteuse en terme de connexion réseaux (data) et de batterie. Afin d’éviter de toujours retélécharger la même photo, par exemple, il serait judicieux de conserver une copie de cette photo en mémoire (cache) ou sur le stockage de l’appareil. La solution parfaite pour ces fonctionnalitée a été trouvé : la labraire Picasso. Elle s’occupe de gérer cette optimisation automatiquement. Pendant le développement, la librairie offre aussi la possibilité d’aouter une indication de couleur sur la photo afin de vérifier la source de l’image : internet, mémoire ou stockage.
Les alertes hitoriques ont causé un peu plus de problème à gérer. OSMdroid a de la difficult, même sur un téléphonne récent, à afficher tous les marqueurs en même temps. L’application ne répond plus très bien aux commandes de l’usager et la mémoire du téléphone devient rapidement surchargée. Ainsi, l’option de les afficher est par défaut désactivée. Si l’usager veut activer cette fonctionnalité, il est prévenu des conséquences sur les donnés consommées que cette décision pourrait engendrer. La mise à jour sur la carte est alors activée. Une requête est envoyée au serveur seulement si un certain zoom est atteint, afin de ne pas surcharger la carte. Les alertes qui sont cherchées sur le serveur sont alors uniquement celles qui se situent à l’intéreur de la zone affiché par l’écran du téléphone, plus un longueur d’écran supplémentaire pour chacune descardinalitités (nord, sud, est, oust). Il s’agit donc d’une dimansion qui correspond à 9x la zone affichée. Tant que l’utilisateur se déplace dansla zone affichée, c’est-à-dire tant qu’il peut observer les alertes comprises dans cette zone élargie, la requête pour des nouvelles alertes au serveur n’Est pas envoyé. Ce n’est qu lorsque le nouveau centre, suite à un déplacement sur la carte, tombe à l’extérieur de la zone de base qu’une nouvelle requête est envoyée. Aussi, un maximum de 256 alertes est conservée en mémoire. Cette limite est implémentée à l’aide d’une structure de donnée de queue. Cette queue retire les éléments les plus anciens qui y ont été insérés si sa capacité maximale est atteinte afin de pouvoir y placer les nouvelles alertes à afficher.

## SOCIAL / KARMA
Dés le départ, l’idée était d’intégrer un système de pointage pour les utilisateurs de l’application. Des inspiration importante sont les systèmes utilisés par les sites et applications tels que Stack Overflow, Google Local Guide ou l’application mobile pour le transposrt en commun Transit. Cette fonctionalité offre plusieurs avantages. En plus de rendre l’utilisation de l’application plus amusante, elle permet d’engager les utilisatuers de manière plus profonde et règle aussi certains problème. Par exemple, imposer un seuil sur le nombre de points avant de pouvoir publier une alerte permet d’éviter d’être inonder de potentielles fausses alertes, puisque les utilisateurs doivent avoir prouvé leur bonnes intentions en participant un minimum. Aussi, le pointage peut servir de motivation pour utiliser pleinement les fonctionalités de l’application. En donnant des points pour l’ajout d’une photo et d’une description assez longue à l’alerte publiée, la base de donnée deviendra mieux documenter.
Comment, alors, intégrer cette idée dans le code. Une recherche de librairies offrant ce genre de fonctionnalités a été fait, mais sans succès. Il faut donc implémenter cette idée à partir de rien … Plusieurs façons de faire sont possible. Naïvement, il est assez simple de vérifier, à chaque opération qui nécéssite un minimum de point, si l’utilisateur courant de l’application a ce niveau. Malheuresement, cette avenue n’est pas maintenable impose un très fort couplage entre l’Application et le module de pointage. Que ce passe-t’il lorsqu’une nouvelle fonctionalité requiert du pointage ? Ou bien qu’une anicenne ne le requiert plus ? Il faut aller changer partout dans le code ! De plus, cette manière de faire va ultimement polluer le le code d’appel à des if(…). Afin de régler certains de ses problèmes et de minimiser le couplage avec l’application, une solution maison a été développé. Il s’agit d’une combinaison d’interface Proxy et d’annotation Java. Au lieu d’utiliser des if(…), une interface est définie dans le code. Les méthodes de cette interface sont annotées avec @KarmaRequired(int) et @KarmaFailed(method = String). Il est alors possible de créer un proxy qui implémente cette interface et l’utiliser pour vérifier, via le module de réflexion de Java, si l’utilisateur courant possède assez de points. L’annotation est alors utilisé et si le nombre de points requis est atteint, la méthode est appelée. Sinon, la méthode annotée @KarmaFailed(method = …) est automatiquement appelée. Ce processus comporte plusieurs avantages. Premièrement, la vérification du nombre de point (karma) que l’uilisateur possède est totalement transparente et n’est codé qu’une seule fois. Il suffit que de créer le proxy est d’appeler les méthodes sur ce proxy au lieu de les appeler sur l’objet en soi. Aussi, les différentes opérations nécéssitant des points peuvent être répartit en plusieurs interfaces différentes et gérées indépendemment. Par exemple IVoteAlertKarma s’occupe uniquement de gérer les opérations en lien avec le vote sur une alerte. Finalement, il est très facile d’ajouter de nouvelles fonctionalités, puisqu’il suffit que de créer un proxy et d’appeler la méthode sur ce proxy plutôt que sur l’objet de base. Toute les autres méthodes qui ne concerne pas le système de pointage ne seront pas affecté.
Toutefois, le système pour donner des points aux usagers n’est pas géré de manière aussi optimale. L’application notifie le serveur à l’aide d’une requête PATCH lorsque nécéssaire et le serveur modifie manuellement la valeur lorsque certaines conditions sont atteintes de son coté (par exemple, une alerte usager attient un certain score et devient alors confirmée). Ce n’est pas aussi maintenable que la vérification effectué dans l’application puisque si une nouvelle fonctionalité doit être intégrée ou qu’une fonctionalité courante doit être modifié ou retirer, tout le code participant à ce processus doit être modifier. Ce n’est pas aussi transparent. Le même genre de système utilisant les annotation spourrait être utilisé. Il faudrait alors qu’il soit intégré autant du coté serveur que client.
La principale fonctionnalité d’intégré et le vote sur les alertes. Les utilisateurs ayant au moins 5 points peuvenet voter +1 ou -1 sur les alertes qu’ils considèrent valables ou non. Plusieurs autres idées pour améliorer le système de pointage ont été consiérée. Par exemple, des badges avec un nombre de point associée pourraient être offertes aux utilisateurs lorsque certaines opération ont été effectué : première alerte publiée, première alerte atteignant 10 points ou une alerte de chaque type publié (eau, feu, terrain, météo). Aussi, des commentaires sur les alertes (avec un système de vote sur le commentaire) pourraint être rajoutées.

## ANDROID
Le défi principal du projet du projet aura été de travailler avec l’API d’Android pour créer des applications mobiles. Puisqu’il s’agit d’une première expérience avec cette plateforme, ce fut un véritable départ à zéro. Le projet aura particulièrement permis de très bien comprendre le fonctionnement des différents Layout utilisé pour l’affichage, le comportement des Activities (élément de base d’une application android) et de comprendre l’implémentation de différentes fonctionalité diverses courantes d’une application mobile.
Layout
Un premier défi fut de travailler avec les layouts fourni par L’API, plus particulièrement le ConstraintLayout pour le design de l’affichage. Ces Layouts sont des fichiers xml et Android Studio (l’IDE de production utilisé pour le projet) offre un environnement visuel pour l’édition de ces fichiers xml afin de vérifier le produit final. Ces fichiers xml sont construits sous formes d’arbre, avec des layouts parents pouvant contenir des enfants et ainsi de suite. Plusieurs options différentes sont offertes pour définir la disposition de l’affichage. Les sources officielles recommandent, pour des raisons de performance et de contrôle du design, le ConstraintLayout. Il permet de définir des marges entre les différents éléments enfants du fichier xml ce qui permet d’avoir un affichage cohérent sur différentes grosseur de téléphone en plus de s’adapter automatiquement au contenu. Il aura par contre fallu, évidemment, apprendre à travailler avec ce ConstraintLayout qui, honnêtement, fonctionne merveilleusement bien une fois la courbe d’apprentissage quelque peu abrupte dépassé. Les infos-bulles et la page d’information ont toute deux été créé à l’aide de ContraintLayouts.
Les ConstraintLayouts n’auront pas été le seul défi pour l’affichage. Intégrer ces layouts dans le code de manière dynamique aura été un défi a bien comprendre. Une fonctionalité existe qui permet de rendre l’affichage plus facilement configurable est les fragments. Ils sont un peu plus difficile à gérer que les Activities mais apprendre à travailler avec eux aura, au final, simplifier certaine opéarations. Par exemple, l’affichage de la plage « plus d’information » est rendue plus dynamique grâce à ces fragments. Le même Layout est utilisé pour les alertes régulières et usagers, mais différents fragments sont rajoutés ou enlevé de l’affichage afin d’intégrer les informations de l’usager de l’alerte, le score de l’alerte, la photo et les usagers ayant votés sur l’alerte.

## Activity
Bien comprendre le fonctionnement internes des Activities est essentiel pour bâtir des applications robustes. Une conception erronnée de la manière dont elles sont gérées aura consé certains problème au début du projet. Ces activités sont conservées en mémoire dans une structure de pile (stack) et sont créées et détruite selon certains paramètres définit. L’erreur , au départ, a été d’associer des informations persitente pour toute la durée de vie de l’application à certaines de ses activitées. Évidemment, ces informations étaient perdues lors de la destruction automatique des instances des activités. Il aura fallu ainsi migrer d’une conception MVC (Modèle-Vue-Controller) vers une l’architecture MVVM (Model-Vue-ViewModel) recomandé pour les applications Android. Cette architecture utilise des ViewModels pour gérer le data contenue en mémoire et faire le lien avec l’affichage. Ces ViewModels permettent d’associer le data persistent aux activitées d’une manière robuste et d’y associer des MutableLiveData. Ces MutableLiveData permettent à leur tour d’utiliser le patron de conception d’observateur afin de modifier automatiquement l’affichage en réponse à un changement. C’est une architecture qui fonctionne très bien et qui a été utilisé pour mettre à jour les alertes montrer sur la carte.

## Fonctionnalité diverses
Le projet a permis de se familiarisé aussi avec plusieurs autres fonctionnalités de base utiles. Il est toutefois parfois difficile de retrouver, parmis la montagne énorme qu’est la documentation, des exemples de code pour la fonctionalité précise recherchée. De plus, les conventions et bonnes pratiques sont presque inexistante, et il faut souvent simplement espérer qu’aucun effet de bord n’est engendré par les différentes opérations effectuées. Malgré tout, plusieurs opérations ont été intégrer tel que l’envoi de couriel, sauvegarder sur la mémoire du téléphone, gérer les préférences de l’application, gérer le niveau de la batterie et de la connexion, les animations et transitions, l’utilisation de l’appareil photo bien d’autres.
Plusieurs de ses fonctionalités sont intégrées dans des modules créées pour l’application. L’idée de ses modules est d’offrir leur fonctionalités plus facilement au reste de l’application et de manière réutilisable. C’est la méthode qui a été utilisé pour minimiser le couplage entre les modules. Ils possèdent habitellement une classe de controlle et rendent accessible uniquement les fonctionalités de base nécéssaires. Ainsi, la communication entre les modules est gérée dans ces controlleurs et le couplage entre les classes est minimisé.