[{"data":1,"prerenderedAt":4515},["ShallowReactive",2],{"tag-posts-android":3},[4,333],{"id":5,"title":6,"author":7,"body":8,"category":300,"categorySlug":301,"date":302,"description":14,"excerpt":303,"extension":317,"location":318,"meta":319,"navigation":320,"path":321,"published":320,"seo":322,"slug":323,"stem":324,"tags":325,"timeToRead":331,"__hash__":332},"posts\u002Fposts\u002FProgrammation\u002F2026-01-29-sportequipement-vibe-coding.md","SportEquipement - Application de suivi de matériel sportif avec Vibe Coding","Ulrich Vandenhekke",{"type":9,"value":10,"toc":290},"minimark",[11,15,18,21,24,32,37,40,43,59,63,66,75,78,81,88,91,94,101,112,116,119,122,136,139,164,167,170,177,180,183,186,189,192,195,199,202,205,208,213,216,219,222,225,229,232,235,238,241,248,252,255,258,261,268,275,278,281,284,287],[12,13,14],"p",{},"Bonjour à tous,",[12,16,17],{},"Pour ceux qui me connaissent, vous savez que je fais un peu de vélo. Et comme tout cycliste moderne, j'utilise Strava.\nC'est l'outil de référence pour suivre ses sorties, comparer ses segments et analyser sa forme. Strava propose bien une\nfonctionnalité de \"Suivi d'équipement\" (pour savoir combien de kilomètres ont vos chaussures ou votre cadre), mais...\nc'est sommaire.",[12,19,20],{},"Si la version Web permet d'ajouter des composants (chaîne, cassette, etc.), l'application mobile a tout simplement\n\"oublié\" cette fonctionnalité.",[12,22,23],{},"J'ai eu quelques problèmes avec ma chaîne qui s'use en 2 000 km ou des plaquettes de frein à changer. Bref j'avais envie\nd'avoir des stats un peu précises sur le suivi de mon matériel. De la même manière je cours régulièrement et je voulais\nsavoir quand mes baskets atteignaient leur durée de vie maximale (ça, ça marche bien sur Strava).",[12,25,26,27,31],{},"Pour améliorer ce suivi j'ai décidé de créer ma propre solution : ",[28,29,30],"strong",{},"SportEquipement",".",[33,34,36],"h2",{"id":35},"sportequipement-cest-quoi","SportEquipement : C'est quoi ?",[12,38,39],{},"L'idée est simple : une application mobile, connectée à Strava et à Android Health Connect, qui récupère mes activités\net incrémente l'usure de chaque composant défini.",[12,41,42],{},"Pour la première version, je voulais aussi qu'elle soit \"Local First\". L'application tourne entièrement sur le téléphone.\nPas de serveur, application open source, et mes données restent chez moi. (Bon, j'ajouterai peut-être dans un futur un\nserveur pour synchroniser mes différents appareils et sécuriser les tokens Strava, mais chaque chose en son temps).",[12,44,45,46,53,54,31],{},"L'application est d'ailleurs disponible sur le ",[47,48,52],"a",{"href":49,"rel":50},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=org.shadoware.sportequipment&hl=fr",[51],"nofollow","Play Store"," et le code source est sur mon ",[47,55,58],{"href":56,"rel":57},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment",[51],"Gitea perso",[33,60,62],{"id":61},"quelques-écrans","Quelques écrans",[12,64,65],{},"Cet écran montre la liste des équipements qui ont été ajoutés. On peut voir l'état d'usure de chaque équipement et si\nl'équipement est usé ou un de ces composants est usé on a une alerte visuelle.",[12,67,68],{},[69,70],"img",{"alt":71,"className":72,"src":74},"Liste des équipements",[73],"img-center","\u002FProgrammation\u002Fsportequipement\u002Fimage1.png",[12,76,77],{},"Sur l'écran détail d'un équipement, on y voit les informations détaillées de l'équipement mais aussi la liste des\ncomposants et l'usure des composants.",[12,79,80],{},"Un composant peut être usé par le kilométrage (ex: chaîne, pneu) ou par le temps (ex: liquide de frein).",[12,82,83],{},[69,84],{"alt":85,"className":86,"src":87},"Liste des composants \u002F Détail",[73],"\u002FProgrammation\u002Fsportequipement\u002Fimage2.png",[12,89,90],{},"Sur l'écran d'activité on récupère la liste des activités synchronisées depuis Strava ou Health Connect (pour l'instant\nune fois par jour). On peut créer des activités manuellement aussi si besoin. C'est l'ajout ou la suppression d'une activité\nqui impacte l'usure des équipements.",[12,92,93],{},"Les activités sont rattachées aux équipements via des types d'activités (l'équipement est attaché à un type d'activité,\net l'activité aussi).",[12,95,96],{},[69,97],{"alt":98,"className":99,"src":100},"Liste des activités",[73],"\u002FProgrammation\u002Fsportequipement\u002Fimage3.png",[12,102,103,104,108,109,31],{},"Mais si je vous écris aujourd'hui, ce n'est pas seulement pour vous présenter l'outil. C'est surtout pour vous raconter\n",[105,106,107],"em",{},"comment"," je l'ai développé. J'ai voulu tester le fameux ",[28,110,111],{},"\"Vibe Coding\"",[33,113,115],{"id":114},"développement-en-vibe-coding","Développement en Vibe Coding",[12,117,118],{},"Je souhaitais développer mon application rapidement. Je voulais également avoir ce que les agents IA avaient dans leurs\nneurones. En effet, j'ai une licence Github Copilot que j'utilisais principalement en mode auto-completion de code, un\npeu pour discuter, et parfois pour modifier du code sur un projet déjà existant. J'ai déjà \"Vibe Codé\" des scripts sans\nimpact dont le but était de faire des exécutions one-shot.",[12,120,121],{},"Sur ce projet, je me suis dit :",[123,124,125,133],"blockquote",{},[12,126,127,128,132],{},"Je ne suis plus développeur, je suis le chef de projet et le product owner. Je décris la fonctionnalité que je veux, et\nl'IA code pour moi. Je teste et si ça ne compile pas ou si ça ne marche pas comme je veux je fais un retour ",[129,130,131],"del",{},"au\ndeveloppeur"," à l'IA.",[12,134,135],{},"A la fin je regarderai le code généré et je verrai ce que l'IA vaut.",[12,137,138],{},"J'ai donc commencé à mettre différents types d'instructions :",[140,141,142,150,157],"ul",{},[143,144,145],"li",{},[47,146,149],{"href":147,"rel":148},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fcopilot-instructions.md",[51],"Expert en Kotlin et développement Android",[143,151,152],{},[47,153,156],{"href":154,"rel":155},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fprompts\u002Fui_ux.prompt.md",[51],"Agent expert en UI\u002FUX",[143,158,159],{},[47,160,163],{"href":161,"rel":162},"https:\u002F\u002Fgogs.shadoware.org\u002FShadowareOrg\u002FSportsEquipment\u002Fsrc\u002Fbranch\u002Fmain\u002F.github\u002Fprompts\u002Fdocumentation.prompt.md",[51],"Expert en documentation technique",[12,165,166],{},"Ces différents experts sont inspirés de prompts trouvés sur Internet et adaptés, modifiés et\u002Fou générés à l'aide de Github\nCopilot également.",[12,168,169],{},"Ensuite j'ai décrit mon projet en quelques lignes dans le prompt. Je lui ai demandé de me faire un plan, puis de le\ndévelopper.",[12,171,172,173,176],{},"Les deux premières semaines ont été grisantes. J'ai développé ",[28,174,175],{},"80% du logiciel"," en un temps record. Je regardais l'IA\ncoder, je cliquais sur le bouton continuer ou approuver (les outils MCP, le build, ...) et je jouais aux jeux vidéo pendant ce\ntemps.",[12,178,179],{},"Pour info les MCP c'est \"Model Context Protocol\", cela permet à Github Copilot d'accéder à des outils comme\nl'execution des commandes, la recherche sur internet, ...",[12,181,182],{},"Ma routine était simple : je demandais une fonctionnalité, l'IA générait le code, je compilais (pendant ce temps je\nfaisais autre chose : jeux vidéos, un autre programme ...). Une fois terminé, je lançais android studio, je compilais,\nje testais.",[12,184,185],{},"Si ça marchait, je passais à la fonctionnalité suivante. Si ça ne marchait pas, je donnais un retour à l'IA en lui\nexpliquant le problème (erreur de compilation, bug, comportement non conforme) et c'était reparti pour un tour.",[12,187,188],{},"Régulièrement je devais réinitialiser la conversation, je demandais donc à l'IA de mettre à jour le plan pour reprendre\ndans une nouvelle conversation. Généralement, je faisais cela quand l'IA commençait à faire n'importe quoi (oubli de ce\nsur quoi on travaillait initialement, pataugeait dans la semoule, ...). Les prémisses du pétage de plombs de l'IA passaient\nsouvent par un passage du français à l'anglais dans les réponses. La signification était probablement la perte du\ncontexte initial et des instructions de démarrage.",[12,190,191],{},"Au début, Copilot m'a pondu une architecture logicielle complexe, découpée en couches bien distinctes. Sur le papier,\nc'était beau (ça vient des instructions expert en développement kotlin qui demande une Clean architecture avec une\nséparation : Domain, Data, Feature, Core).",[12,193,194],{},"De mon coté, pour un projet perso, si j'avais dû développer le projet moi-même de zéro, j'aurais probablement fait\nbeaucoup plus simple et direct. Probablement une application avec des vues, une couche service et une couche DAO dans le\nmême projet. Mais là, c'était l'IA qui gérait l'architecture, donc je laissais faire. J'avançais à l'aveugle.",[33,196,198],{"id":197},"et-là-cest-le-drame","Et là, c'est le drame",[12,200,201],{},"Au bout de deux semaines, patatras. Je reçois une notification : je n'ai plus de crédits GitHub Copilot pour les\nrequêtes premium. Panne sèche.",[12,203,204],{},"J'ai bien tenté de reprendre avec un GPT4.1 ou GPT4o qui sont inclus en illimité, mais quand on a testé Gemini 3 ou\nClaude Sonnet 4.... qu'est que GPT4 est con....",[12,206,207],{},"Je me retrouve seul face à mon IDE, sans l'aide de copilot. J'étais le 15 du mois et il me restait 2 semaines avant le\nrenouvellement. Je me suis dit :",[123,209,210],{},[12,211,212],{},"Bon, c'est l'occasion de regarder un peu ce qu'il m'a écrit en détail et de faire une revue de code.",[12,214,215],{},"J'ai ouvert les fichiers. Et là... j'ai eu du sang qui m'a coulé des yeux, la cervelle qui m'est sortie par les oreilles ...",[12,217,218],{},"Le MCD (Modèle Conceptuel de Données) était inutilement complexe. Il y avait de la duplication de code partout. Pour\nfaire la même chose, l'IA avait parfois utilisé trois méthodes différentes à trois endroits du code, sans aucune\ncohérence globale. Il y avait du code mort, des variables inutilisées, des détours techniques incompréhensibles.",[12,220,221],{},"C'était fonctionnel, oui. Mais c'était sale. Très sale.",[12,223,224],{},"J'ai donc commencé à lister tous les problèmes architecturaux que j'ai vus dans le code (et attention il y en a\nprobablement d'autres). La suppression du code mort, je l'ai faite moi-même car à chaque fois l'IA n'était pas capable\nde le détecter si je ne lui donne pas le nom de la méthode.",[33,226,228],{"id":227},"le-grand-nettoyage","Le grand nettoyage",[12,230,231],{},"Dès que mes crédits ont été renouvelés, j'ai voulu tout nettoyer et donner à l'IA l'ensemble de ma revue de code.\nOptimiste, j'ai donné le grand listing de tout ce qui n'allait pas et de demander à l'IA une correction globale.",[12,233,234],{},"Échec total. C'était trop pour elle. Elle se perdait dans les corrections, me disait que tout était fait, mais seule une\npartie était terminée....",[12,236,237],{},"J'ai dû changer de stratégie. J'ai repris mon \"Vibe Coding\", mais cette fois-ci pour réparer les dégâts, problème par\nproblème, couche par couche (domaine, puis data, puis feature, puis core).",[12,239,240],{},"Puis je suis repassé en mode petite itération pour terminer les dernières fonctionnalités, corriger les derniers bugs. Et\nj'ai parfois dû mettre les mains dans le cambouis moi-même pour corriger des problèmes que l'IA n'arrivait pas à\nrésoudre.",[12,242,243,244,247],{},"Résultat des courses : j'avais fait 80% du projet (le prototype sale) en 2 semaines. Il m'a fallu ",[28,245,246],{},"un mois entier","\npour faire les 20% restants et nettoyer la base de code pour la rendre un peu plus propre.",[33,249,251],{"id":250},"conclusion","Conclusion",[12,253,254],{},"Aujourd'hui, l'application est sur le store Google, elle fonctionne bien, et elle répond à mon besoin initial.",[12,256,257],{},"Mais je ressors de cette expérience avec un sentiment mitigé. J'ai un étrange détachement vis-à-vis de ce projet.",[12,259,260],{},"J'ai l'impression d'avoir codé avec un stagiaire qui a de très bonnes connaissances théoriques sur l'architecture logicielle\nmais qui sans contrôle te crée une dette technique immense. Par contre il a l'avantage d'écrire très vite. Ce projet qui\na été fait en un mois - un mois et demi, m'aurait pris beaucoup plus de temps (sachant que je ne développe en perso que\nle soir et le week-end).",[12,262,263,264,267],{},"D'habitude, quand je code un logiciel, c'est mon \"bébé\". Je connais chaque ligne, chaque astuce, chaque défaut. Là...\nj'ai l'impression que ce n'est pas ",[28,265,266],{},"mon"," projet. Je me sens comme le Chef de Projet ou le client qui a passé commande.",[12,269,270,271,274],{},"Pour les petits scripts, les projets ",[28,272,273],{},"utilitaires",", l'intelligence artificielle est super pratique. Elle permet de\nconstruire rapidement un prototype fonctionnel. Mais sans maîtrise, le code n'est pas maintenable dans le temps (même\npar l'IA elle-même). Et je ne parle pas non plus des problématiques de sécurité, si j'avais un serveur sur cette\napplication.",[12,276,277],{},"Pour les projets de passionnés, ceux qu'on fait \"pour l'art\" ou pour le plaisir d'apprendre, je ne suis pas sûr de\nréitérer l'expérience aussi poussée. J'aime développer et c'est important pour moi de toucher la ligne de code.",[12,279,280],{},"J'ai aussi une autre inquiétude par rapport à l'avenir du métier de développeur. Un bon développeur pour l'instant est\ntoujours nécessaire pour superviser l'intelligence artificielle.",[12,282,283],{},"Mais les juniors qui arrivent sur le marché du travail vont-ils apprendre les bonnes pratiques de développement s'ils\nse reposent trop sur l'IA pour coder à leur place ? Est-ce que les juniors vont trouver du travail, même s'ils sont\ncompétents ?",[12,285,286],{},"Actuellement un développeur qui n'utilise pas l'IA risque de se retrouver à un moment dépassé sur le marché du travail.\nCe que j'aimais avec le développement c'est qu'avant n'importe qui pouvait faire du développement avec n'importe quel\nPC. Il y avait un coût d'apprentissage, tout le monde n'est pas fait pour faire du développement, mais c'était accessible\nfinancièrement aux personnes motivées.",[12,288,289],{},"Maintenant, il faut soit une très grosse carte graphique (et même là ce ne sont pas des modèles ultra performants), ou\nun abonnement payant. Il y a quelques versions gratuites mais elles sont là pour attirer. L'IA rend le développement plus\naccessible au niveau apprentissage, mais moins accessible financièrement et surtout dépendant d'un fournisseur.",{"title":291,"searchDepth":292,"depth":292,"links":293},"",2,[294,295,296,297,298,299],{"id":35,"depth":292,"text":36},{"id":61,"depth":292,"text":62},{"id":114,"depth":292,"text":115},{"id":197,"depth":292,"text":198},{"id":227,"depth":292,"text":228},{"id":250,"depth":292,"text":251},"Programmation","programmation","2026-01-29",{"type":9,"value":304},[305,307,309,311,313],[12,306,14],{},[12,308,17],{},[12,310,20],{},[12,312,23],{},[12,314,26,315,31],{},[28,316,30],{},"md","Lille, France",{"planet":320},true,"\u002Fpost\u002Fsportequipement-vibe-coding",{"title":6,"description":14},"sportequipement-vibe-coding","posts\u002FProgrammation\u002F2026-01-29-sportequipement-vibe-coding",[326,327,328,329,330],"developpement","ia","vibe coding","rex","android",10,"UjPK0msj1uJoiDCVdG4PiDDZUq_PLpB0i2SzFIOesPI",{"id":334,"title":335,"author":7,"body":336,"category":300,"categorySlug":301,"date":4474,"description":340,"excerpt":4475,"extension":317,"location":318,"meta":4507,"navigation":320,"path":4508,"published":320,"seo":4509,"slug":4510,"stem":4511,"tags":4512,"timeToRead":769,"__hash__":4514},"posts\u002Fposts\u002FProgrammation\u002F2020-11-14_application_du_confinement.md","L'Application du confinement pour se déplacer",{"type":9,"value":337,"toc":4464},[338,341,344,353,361,364,367,370,384,388,407,410,429,432,435,438,445,448,453,459,589,592,883,886,889,892,1319,1333,1342,1345,1533,1536,1672,1676,1679,1682,1748,1759,1803,1806,1919,1922,2166,2169,2439,2442,2700,2718,2729,2733,2740,2746,2869,2883,2886,3050,3053,3059,3066,3069,3355,3358,3361,3465,3475,3485,3819,3822,3828,4383,4386,4390,4393,4408,4411,4427,4460],[12,339,340],{},"Préambule: A cause du temps de validation du PlayStore, je publie cet article avec une semaine de retard.",[12,342,343],{},"Cela fait plus d'une semaine (quand j'écris ces lignes) que le re-confinement à commencé. Quand je vais courir, je dois me cantonner à\n1km autour de chez moi. Mais quand je cours j'aimerais que mon téléphone intelligent me prévienne quand j'approche du rayon de 1km ou\nquand je le dépasse. Je ne souhaite pas avoir le nez sur une carte de mon téléphone.",[12,345,346,347,352],{},"Je regarde ce qui se fait. J'ai trouvé l'application suivante sur le play store : ",[47,348,351],{"href":349,"rel":350},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.BETechnology.unkm",[51],"1km",".\nL'application m'avait l'air de répondre à mes critères mais ne fonctionnait pas lors de mon utilisation (en plus il y avait de la pub).",[12,354,355,356,360],{},"Une autre application ",[47,357,351],{"href":358,"rel":359},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.apps100101.a1km",[51]," pourrait répondre à mon besoin mais\nje ne l'ai pas testé.",[12,362,363],{},"Beaucoup d'applications ont pour but de dessiner un cercle sur une carte.",[12,365,366],{},"J'ai finalement décidé d'écrire ma propre application (en plus elle sera open source).",[12,368,369],{},"Voici ce dont j'ai besoin:",[140,371,372,375,378,381],{},[143,373,374],{},"une application open source",[143,376,377],{},"un gros bouton pour démarrer la surveillance (à partir de mon point de départ)",[143,379,380],{},"un avertissement quand on approche du rayon max des 1km.",[143,382,383],{},"un avertissement régulier qand on dépasse les 1km.",[33,385,387],{"id":386},"lapplication","L'application",[12,389,390,391,394,395,398,399,402,403,406],{},"Pour développer une application Android je vais démarrer ",[105,392,393],{},"Android Studio"," et commencer à développer le 1er écran. Je choisis le\nlanguage ",[105,396,397],{},"Java"," que je maîtrise plus que le language ",[105,400,401],{},"Kotlin"," et le SDK Minimum de ",[105,404,405],{},"Android 6.0"," pour toucher 85% des\nutilisateurs (si l'application peut interesser d'autres personnes).",[12,408,409],{},"J'imagine l'application découpée en deux parties:",[140,411,412,426],{},[143,413,414,415,425],{},"L'activité",[416,417,418],"sup",{},[47,419,424],{"href":420,"ariaDescribedBy":421,"dataFootnoteRef":291,"id":423},"#user-content-fn-1",[422],"footnote-label","user-content-fnref-1","1"," principale de l'application qui contiendra mon gros bouton, la position de départ, la position courante, et la\ndistance à vol d'oiseau du point de départ.",[143,427,428],{},"Un service, dont le but est quand l'application est démarrée, de surveiller les déplacements et d'informer l'utilisateur.",[12,430,431],{},"Nous allons donc commencer par développer l'activité",[33,433,414],{"id":434},"lactivité",[12,436,437],{},"Je ne suis pas graphiste ni UI\u002FUX designer. Le design de cette première interface va alors être très simple et très sobre. Un\ngros bouton + les différentes informations dont j'ai besoin quand l'application est démarrée :",[12,439,440],{},[69,441],{"alt":442,"className":443,"src":444},"Screenshoot page principale",[73],"\u002FProgrammation\u002F1kmarround\u002Fscreenshoot1.png",[12,446,447],{},"Lors du démarrage de l'application, j'ai besoin que celle-ci écoute le changement, les positions de l'utilisateur afin de\ndéfinir le point de départ. Comme ce service d'écoute me sera utile également pour le service, je développe une classe à coté.",[449,450,452],"h3",{"id":451},"arroundlocationmanager","ArroundLocationManager",[12,454,455,456,458],{},"Voici donc la classe ",[28,457,452],{},":",[460,461,465],"pre",{"className":462,"code":463,"language":464,"meta":291,"style":291},"language-java shiki shiki-themes one-dark-pro","class ArroundLocationManager extends Thread {\n    private static final String TAG = \"ArroundLocationManager\";\n\n    private LocationManager mLocationManager = null;\n    private static final int LOCATION_INTERVAL = 1000;\n    private static final float LOCATION_DISTANCE = 50f;\n","java",[466,467,468,491,520,526,545,567],"code",{"__ignoreMap":291},[469,470,473,477,481,484,487],"span",{"class":471,"line":472},"line",1,[469,474,476],{"class":475},"seHd6","class",[469,478,480],{"class":479},"sU0A5"," ArroundLocationManager",[469,482,483],{"class":475}," extends",[469,485,486],{"class":479}," Thread",[469,488,490],{"class":489},"sn6KH"," {\n",[469,492,493,496,499,502,505,509,513,517],{"class":471,"line":292},[469,494,495],{"class":475},"    private",[469,497,498],{"class":475}," static",[469,500,501],{"class":475}," final",[469,503,504],{"class":479}," String",[469,506,508],{"class":507},"sVyAn"," TAG ",[469,510,512],{"class":511},"sjrmR","=",[469,514,516],{"class":515},"subq3"," \"ArroundLocationManager\"",[469,518,519],{"class":489},";\n",[469,521,523],{"class":471,"line":522},3,[469,524,525],{"emptyLinePlaceholder":320},"\n",[469,527,529,531,534,537,539,543],{"class":471,"line":528},4,[469,530,495],{"class":475},[469,532,533],{"class":479}," LocationManager",[469,535,536],{"class":507}," mLocationManager ",[469,538,512],{"class":511},[469,540,542],{"class":541},"sVC51"," null",[469,544,519],{"class":489},[469,546,548,550,552,554,557,560,562,565],{"class":471,"line":547},5,[469,549,495],{"class":475},[469,551,498],{"class":475},[469,553,501],{"class":475},[469,555,556],{"class":475}," int",[469,558,559],{"class":507}," LOCATION_INTERVAL ",[469,561,512],{"class":511},[469,563,564],{"class":541}," 1000",[469,566,519],{"class":489},[469,568,570,572,574,576,579,582,584,587],{"class":471,"line":569},6,[469,571,495],{"class":475},[469,573,498],{"class":475},[469,575,501],{"class":475},[469,577,578],{"class":475}," float",[469,580,581],{"class":507}," LOCATION_DISTANCE ",[469,583,512],{"class":511},[469,585,586],{"class":541}," 50f",[469,588,519],{"class":489},[12,590,591],{},"Pour commencer définissons quelques constantes: je souhaite avoir la position, tous les 50m et au maximum toutes les secondes\n(m'enfin quelqu'un qui fait plus de 50m en une seconde à pied est trop fort pour moi).",[460,593,595],{"className":462,"code":594,"language":464,"meta":291,"style":291},"    private List\u003CArroundLocationManager.ArrroundLocationListener> listener = new ArrayList\u003C>();\n\n    public interface ArrroundLocationListener {\n        void updateLocation(Location startLocation);\n    }\n\n    private ArroundLocationManager.LocationListener[] mLocationListeners = new ArroundLocationManager.LocationListener[]{\n            new ArroundLocationManager.LocationListener(LocationManager.GPS_PROVIDER),\n            new ArroundLocationManager.LocationListener(LocationManager.NETWORK_PROVIDER)\n    };\n\n    public void addListener(ArroundLocationManager.ArrroundLocationListener l) {\n        listener.add(l);\n    }\n\n    private void callListener(Location location) {\n        for( ArroundLocationManager.ArrroundLocationListener l : listener) {\n            l.updateLocation(location);\n        }\n    }\n",[466,596,597,636,640,653,675,680,684,712,737,760,767,772,794,808,813,818,835,858,872,878],{"__ignoreMap":291},[469,598,599,601,604,607,609,611,614,617,620,622,625,628,631,634],{"class":471,"line":472},[469,600,495],{"class":475},[469,602,603],{"class":479}," List",[469,605,606],{"class":489},"\u003C",[469,608,452],{"class":479},[469,610,31],{"class":489},[469,612,613],{"class":479},"ArrroundLocationListener",[469,615,616],{"class":489},">",[469,618,619],{"class":507}," listener ",[469,621,512],{"class":511},[469,623,624],{"class":475}," new",[469,626,627],{"class":479}," ArrayList",[469,629,630],{"class":489},"\u003C>",[469,632,633],{"class":507},"()",[469,635,519],{"class":489},[469,637,638],{"class":471,"line":292},[469,639,525],{"emptyLinePlaceholder":320},[469,641,642,645,648,651],{"class":471,"line":522},[469,643,644],{"class":475},"    public",[469,646,647],{"class":475}," interface",[469,649,650],{"class":479}," ArrroundLocationListener",[469,652,490],{"class":489},[469,654,655,658,662,665,668,672],{"class":471,"line":528},[469,656,657],{"class":475},"        void",[469,659,661],{"class":660},"sVbv2"," updateLocation",[469,663,664],{"class":489},"(",[469,666,667],{"class":479},"Location",[469,669,671],{"class":670},"s_ZVi"," startLocation",[469,673,674],{"class":489},");\n",[469,676,677],{"class":471,"line":547},[469,678,679],{"class":489},"    }\n",[469,681,682],{"class":471,"line":569},[469,683,525],{"emptyLinePlaceholder":320},[469,685,687,689,691,693,696,699,701,703,705,707,709],{"class":471,"line":686},7,[469,688,495],{"class":475},[469,690,480],{"class":479},[469,692,31],{"class":489},[469,694,695],{"class":479},"LocationListener",[469,697,698],{"class":507},"[] mLocationListeners ",[469,700,512],{"class":511},[469,702,624],{"class":475},[469,704,480],{"class":479},[469,706,31],{"class":489},[469,708,695],{"class":479},[469,710,711],{"class":507},"[]{\n",[469,713,715,718,720,722,724,726,729,731,734],{"class":471,"line":714},8,[469,716,717],{"class":475},"            new",[469,719,480],{"class":507},[469,721,31],{"class":489},[469,723,695],{"class":660},[469,725,664],{"class":489},[469,727,728],{"class":479},"LocationManager",[469,730,31],{"class":489},[469,732,733],{"class":479},"GPS_PROVIDER",[469,735,736],{"class":489},"),\n",[469,738,740,742,744,746,748,750,752,754,757],{"class":471,"line":739},9,[469,741,717],{"class":475},[469,743,480],{"class":507},[469,745,31],{"class":489},[469,747,695],{"class":660},[469,749,664],{"class":489},[469,751,728],{"class":479},[469,753,31],{"class":489},[469,755,756],{"class":479},"NETWORK_PROVIDER",[469,758,759],{"class":489},")\n",[469,761,762,765],{"class":471,"line":331},[469,763,764],{"class":507},"    }",[469,766,519],{"class":489},[469,768,770],{"class":471,"line":769},11,[469,771,525],{"emptyLinePlaceholder":320},[469,773,775,777,780,783,785,787,789,791],{"class":471,"line":774},12,[469,776,644],{"class":475},[469,778,779],{"class":475}," void",[469,781,782],{"class":660}," addListener",[469,784,664],{"class":507},[469,786,452],{"class":479},[469,788,31],{"class":489},[469,790,613],{"class":479},[469,792,793],{"class":507}," l) {\n",[469,795,797,800,802,805],{"class":471,"line":796},13,[469,798,799],{"class":479},"        listener",[469,801,31],{"class":489},[469,803,804],{"class":660},"add",[469,806,807],{"class":489},"(l);\n",[469,809,811],{"class":471,"line":810},14,[469,812,679],{"class":507},[469,814,816],{"class":471,"line":815},15,[469,817,525],{"emptyLinePlaceholder":320},[469,819,821,823,825,828,830,832],{"class":471,"line":820},16,[469,822,495],{"class":475},[469,824,779],{"class":475},[469,826,827],{"class":660}," callListener",[469,829,664],{"class":507},[469,831,667],{"class":479},[469,833,834],{"class":507}," location) {\n",[469,836,838,841,844,846,848,850,853,855],{"class":471,"line":837},17,[469,839,840],{"class":475},"        for",[469,842,843],{"class":507},"( ",[469,845,452],{"class":479},[469,847,31],{"class":489},[469,849,613],{"class":479},[469,851,852],{"class":507}," l ",[469,854,458],{"class":475},[469,856,857],{"class":507}," listener) {\n",[469,859,861,864,866,869],{"class":471,"line":860},18,[469,862,863],{"class":479},"            l",[469,865,31],{"class":489},[469,867,868],{"class":660},"updateLocation",[469,870,871],{"class":489},"(location);\n",[469,873,875],{"class":471,"line":874},19,[469,876,877],{"class":507},"        }\n",[469,879,881],{"class":471,"line":880},20,[469,882,679],{"class":507},[12,884,885],{},"Viens ensuite la définition d'un listener pour que les applications qui s'abonnent à cette classe puissent bénéficier d'un\nlistener et recevoir des notifications lors de la mise à jour des positions.",[12,887,888],{},"Comme on peut le constater je fais tourner cette classe dans un thread. Lors de mon développement je me suis rendu compte que\nlorsque je quittais l'application, le service était tué également. Une des raisons à cela est que le service est dans le même\nthread que l'activité principale. L'ajout de ce thread (ainsi que d'autre chose) ont résolu le problème. (Mais il est possible\nque ce soit plus lié aux autres choses qu'au thread lui même).",[12,890,891],{},"Voici le coeur du thread:",[460,893,895],{"className":462,"code":894,"language":464,"meta":291,"style":291},"    public void run() {\n        Looper.prepare();\n\n        initializeLocationManager();\n\n        Looper.loop();\n\n        \u002F\u002F Never called, loop is killed when activity or thread stopped\n        finalizeLocationManager();\n    }\n\n    private ArroundLocationManager.LocationListener[] mLocationListeners = new ArroundLocationManager.LocationListener[]{\n        new ArroundLocationManager.LocationListener(LocationManager.GPS_PROVIDER),\n        new ArroundLocationManager.LocationListener(LocationManager.NETWORK_PROVIDER)\n    };\n\n    public void initializeLocationManager() {\n        try {\n            mLocationManager.requestLocationUpdates(\n                    LocationManager.NETWORK_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,\n                    mLocationListeners[1]);\n        } catch (java.lang.SecurityException ex) {\n            Log.i(TAG, \"fail to request location update, ignore\", ex);\n        } catch (IllegalArgumentException ex) {\n            Log.d(TAG, \"network provider does not exist, \" + ex.getMessage());\n        }\n\n        try {\n            mLocationManager.requestLocationUpdates(\n                    LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,\n                    mLocationListeners[0]);\n        } catch (java.lang.SecurityException ex) {\n            Log.i(TAG, \"fail to request location update, ignore\", ex);\n        } catch (IllegalArgumentException ex) {\n            Log.d(TAG, \"gps provider does not exist \" + ex.getMessage());\n        }\n    }\n",[466,896,897,909,922,926,935,939,950,954,960,969,973,977,1001,1022,1042,1048,1052,1063,1070,1083,1095,1106,1127,1147,1163,1191,1196,1201,1208,1219,1230,1240,1255,1270,1285,1309,1314],{"__ignoreMap":291},[469,898,899,901,903,906],{"class":471,"line":472},[469,900,644],{"class":475},[469,902,779],{"class":475},[469,904,905],{"class":660}," run",[469,907,908],{"class":507},"() {\n",[469,910,911,914,916,919],{"class":471,"line":292},[469,912,913],{"class":479},"        Looper",[469,915,31],{"class":489},[469,917,918],{"class":660},"prepare",[469,920,921],{"class":489},"();\n",[469,923,924],{"class":471,"line":522},[469,925,525],{"emptyLinePlaceholder":320},[469,927,928,931,933],{"class":471,"line":528},[469,929,930],{"class":660},"        initializeLocationManager",[469,932,633],{"class":507},[469,934,519],{"class":489},[469,936,937],{"class":471,"line":547},[469,938,525],{"emptyLinePlaceholder":320},[469,940,941,943,945,948],{"class":471,"line":569},[469,942,913],{"class":479},[469,944,31],{"class":489},[469,946,947],{"class":660},"loop",[469,949,921],{"class":489},[469,951,952],{"class":471,"line":686},[469,953,525],{"emptyLinePlaceholder":320},[469,955,956],{"class":471,"line":714},[469,957,959],{"class":958},"sV9Aq","        \u002F\u002F Never called, loop is killed when activity or thread stopped\n",[469,961,962,965,967],{"class":471,"line":739},[469,963,964],{"class":660},"        finalizeLocationManager",[469,966,633],{"class":507},[469,968,519],{"class":489},[469,970,971],{"class":471,"line":331},[469,972,679],{"class":507},[469,974,975],{"class":471,"line":769},[469,976,525],{"emptyLinePlaceholder":320},[469,978,979,981,983,985,987,989,991,993,995,997,999],{"class":471,"line":774},[469,980,495],{"class":475},[469,982,480],{"class":479},[469,984,31],{"class":489},[469,986,695],{"class":479},[469,988,698],{"class":507},[469,990,512],{"class":511},[469,992,624],{"class":475},[469,994,480],{"class":479},[469,996,31],{"class":489},[469,998,695],{"class":479},[469,1000,711],{"class":507},[469,1002,1003,1006,1008,1010,1012,1014,1016,1018,1020],{"class":471,"line":796},[469,1004,1005],{"class":475},"        new",[469,1007,480],{"class":507},[469,1009,31],{"class":489},[469,1011,695],{"class":660},[469,1013,664],{"class":489},[469,1015,728],{"class":479},[469,1017,31],{"class":489},[469,1019,733],{"class":479},[469,1021,736],{"class":489},[469,1023,1024,1026,1028,1030,1032,1034,1036,1038,1040],{"class":471,"line":810},[469,1025,1005],{"class":475},[469,1027,480],{"class":507},[469,1029,31],{"class":489},[469,1031,695],{"class":660},[469,1033,664],{"class":489},[469,1035,728],{"class":479},[469,1037,31],{"class":489},[469,1039,756],{"class":479},[469,1041,759],{"class":489},[469,1043,1044,1046],{"class":471,"line":815},[469,1045,764],{"class":507},[469,1047,519],{"class":489},[469,1049,1050],{"class":471,"line":820},[469,1051,525],{"emptyLinePlaceholder":320},[469,1053,1054,1056,1058,1061],{"class":471,"line":837},[469,1055,644],{"class":475},[469,1057,779],{"class":475},[469,1059,1060],{"class":660}," initializeLocationManager",[469,1062,908],{"class":507},[469,1064,1065,1068],{"class":471,"line":860},[469,1066,1067],{"class":475},"        try",[469,1069,490],{"class":507},[469,1071,1072,1075,1077,1080],{"class":471,"line":874},[469,1073,1074],{"class":479},"            mLocationManager",[469,1076,31],{"class":489},[469,1078,1079],{"class":660},"requestLocationUpdates",[469,1081,1082],{"class":489},"(\n",[469,1084,1085,1088,1090,1092],{"class":471,"line":880},[469,1086,1087],{"class":479},"                    LocationManager",[469,1089,31],{"class":489},[469,1091,756],{"class":479},[469,1093,1094],{"class":489},", LOCATION_INTERVAL, LOCATION_DISTANCE,\n",[469,1096,1098,1101,1103],{"class":471,"line":1097},21,[469,1099,1100],{"class":489},"                    mLocationListeners[",[469,1102,424],{"class":541},[469,1104,1105],{"class":489},"]);\n",[469,1107,1109,1112,1115,1118,1121,1124],{"class":471,"line":1108},22,[469,1110,1111],{"class":507},"        } ",[469,1113,1114],{"class":475},"catch",[469,1116,1117],{"class":507}," (",[469,1119,1120],{"class":479},"java.lang.SecurityException",[469,1122,1123],{"class":670}," ex",[469,1125,1126],{"class":507},") {\n",[469,1128,1130,1133,1135,1138,1141,1144],{"class":471,"line":1129},23,[469,1131,1132],{"class":479},"            Log",[469,1134,31],{"class":489},[469,1136,1137],{"class":660},"i",[469,1139,1140],{"class":489},"(TAG, ",[469,1142,1143],{"class":515},"\"fail to request location update, ignore\"",[469,1145,1146],{"class":489},", ex);\n",[469,1148,1150,1152,1154,1156,1159,1161],{"class":471,"line":1149},24,[469,1151,1111],{"class":507},[469,1153,1114],{"class":475},[469,1155,1117],{"class":507},[469,1157,1158],{"class":479},"IllegalArgumentException",[469,1160,1123],{"class":670},[469,1162,1126],{"class":507},[469,1164,1166,1168,1170,1173,1175,1178,1181,1183,1185,1188],{"class":471,"line":1165},25,[469,1167,1132],{"class":479},[469,1169,31],{"class":489},[469,1171,1172],{"class":660},"d",[469,1174,1140],{"class":489},[469,1176,1177],{"class":515},"\"network provider does not exist, \"",[469,1179,1180],{"class":511}," +",[469,1182,1123],{"class":479},[469,1184,31],{"class":489},[469,1186,1187],{"class":660},"getMessage",[469,1189,1190],{"class":489},"());\n",[469,1192,1194],{"class":471,"line":1193},26,[469,1195,877],{"class":507},[469,1197,1199],{"class":471,"line":1198},27,[469,1200,525],{"emptyLinePlaceholder":320},[469,1202,1204,1206],{"class":471,"line":1203},28,[469,1205,1067],{"class":475},[469,1207,490],{"class":507},[469,1209,1211,1213,1215,1217],{"class":471,"line":1210},29,[469,1212,1074],{"class":479},[469,1214,31],{"class":489},[469,1216,1079],{"class":660},[469,1218,1082],{"class":489},[469,1220,1222,1224,1226,1228],{"class":471,"line":1221},30,[469,1223,1087],{"class":479},[469,1225,31],{"class":489},[469,1227,733],{"class":479},[469,1229,1094],{"class":489},[469,1231,1233,1235,1238],{"class":471,"line":1232},31,[469,1234,1100],{"class":489},[469,1236,1237],{"class":541},"0",[469,1239,1105],{"class":489},[469,1241,1243,1245,1247,1249,1251,1253],{"class":471,"line":1242},32,[469,1244,1111],{"class":507},[469,1246,1114],{"class":475},[469,1248,1117],{"class":507},[469,1250,1120],{"class":479},[469,1252,1123],{"class":670},[469,1254,1126],{"class":507},[469,1256,1258,1260,1262,1264,1266,1268],{"class":471,"line":1257},33,[469,1259,1132],{"class":479},[469,1261,31],{"class":489},[469,1263,1137],{"class":660},[469,1265,1140],{"class":489},[469,1267,1143],{"class":515},[469,1269,1146],{"class":489},[469,1271,1273,1275,1277,1279,1281,1283],{"class":471,"line":1272},34,[469,1274,1111],{"class":507},[469,1276,1114],{"class":475},[469,1278,1117],{"class":507},[469,1280,1158],{"class":479},[469,1282,1123],{"class":670},[469,1284,1126],{"class":507},[469,1286,1288,1290,1292,1294,1296,1299,1301,1303,1305,1307],{"class":471,"line":1287},35,[469,1289,1132],{"class":479},[469,1291,31],{"class":489},[469,1293,1172],{"class":660},[469,1295,1140],{"class":489},[469,1297,1298],{"class":515},"\"gps provider does not exist \"",[469,1300,1180],{"class":511},[469,1302,1123],{"class":479},[469,1304,31],{"class":489},[469,1306,1187],{"class":660},[469,1308,1190],{"class":489},[469,1310,1312],{"class":471,"line":1311},36,[469,1313,877],{"class":507},[469,1315,1317],{"class":471,"line":1316},37,[469,1318,679],{"class":507},[12,1320,1321,1322,1324,1325,1328,1329,1332],{},"On utilise le service ",[466,1323,728],{}," d'android pour écouter la position de l'utilisateur. On écoute la position venant\ndu ",[105,1326,1327],{},"Network"," qui permet d'avoir une position moins fiable mais rapide, puis celle venant du ",[105,1330,1331],{},"GPS"," permettant d'avoir une\nposition fiable (mais lente à obtenir).",[12,1334,1335,1336,1341],{},"Afin d'avoir une position la plus précise aussi, je me suis inspiré du code suivant:\n",[47,1337,1340],{"href":1338,"rel":1339},"https:\u002F\u002Fstuff.mit.edu\u002Fafs\u002Fsipb\u002Fproject\u002Fandroid\u002Fdocs\u002Ftraining\u002Fbasics\u002Flocation\u002Fcurrentlocation.html",[51],"Obtaining the Current Location",".\nLe code en question permet de choisir entre deux positions la plus précise (entre la position NETWORK et la position GPS).",[12,1343,1344],{},"On notifie les appelants:",[460,1346,1348],{"className":462,"code":1347,"language":464,"meta":291,"style":291},"    private class LocationListener implements android.location.LocationListener {\n        public LocationListener(String provider) {\n            Log.e(TAG, \"LocationListener \" + provider);\n            mLocation = new Location(provider);\n        }\n\n        @Override\n        public void onLocationChanged(Location location) {\n            Log.e(TAG, \"onLocationChanged: \" + location);\n            if (isBetterLocation(location, mLocation)) {\n                mLocation.set(location);\n            }\n\n            callListener(mLocation);\n        }\n        ...\n    }\n",[466,1349,1350,1370,1390,1409,1424,1428,1432,1440,1460,1478,1491,1503,1508,1512,1520,1524,1529],{"__ignoreMap":291},[469,1351,1352,1354,1357,1360,1363,1366,1368],{"class":471,"line":472},[469,1353,495],{"class":475},[469,1355,1356],{"class":475}," class",[469,1358,1359],{"class":479}," LocationListener",[469,1361,1362],{"class":475}," implements",[469,1364,1365],{"class":507}," android.location.",[469,1367,695],{"class":479},[469,1369,490],{"class":489},[469,1371,1372,1375,1377,1379,1382,1385,1388],{"class":471,"line":292},[469,1373,1374],{"class":475},"        public",[469,1376,1359],{"class":660},[469,1378,664],{"class":489},[469,1380,1381],{"class":479},"String",[469,1383,1384],{"class":670}," provider",[469,1386,1387],{"class":489},")",[469,1389,490],{"class":489},[469,1391,1392,1394,1396,1399,1401,1404,1406],{"class":471,"line":522},[469,1393,1132],{"class":479},[469,1395,31],{"class":489},[469,1397,1398],{"class":660},"e",[469,1400,1140],{"class":489},[469,1402,1403],{"class":515},"\"LocationListener \"",[469,1405,1180],{"class":511},[469,1407,1408],{"class":489}," provider);\n",[469,1410,1411,1414,1416,1418,1421],{"class":471,"line":528},[469,1412,1413],{"class":489},"            mLocation ",[469,1415,512],{"class":511},[469,1417,624],{"class":475},[469,1419,1420],{"class":660}," Location",[469,1422,1423],{"class":489},"(provider);\n",[469,1425,1426],{"class":471,"line":547},[469,1427,877],{"class":489},[469,1429,1430],{"class":471,"line":569},[469,1431,525],{"emptyLinePlaceholder":320},[469,1433,1434,1437],{"class":471,"line":686},[469,1435,1436],{"class":489},"        @",[469,1438,1439],{"class":479},"Override\n",[469,1441,1442,1444,1446,1449,1451,1453,1456,1458],{"class":471,"line":714},[469,1443,1374],{"class":475},[469,1445,779],{"class":475},[469,1447,1448],{"class":660}," onLocationChanged",[469,1450,664],{"class":489},[469,1452,667],{"class":479},[469,1454,1455],{"class":670}," location",[469,1457,1387],{"class":489},[469,1459,490],{"class":489},[469,1461,1462,1464,1466,1468,1470,1473,1475],{"class":471,"line":739},[469,1463,1132],{"class":479},[469,1465,31],{"class":489},[469,1467,1398],{"class":660},[469,1469,1140],{"class":489},[469,1471,1472],{"class":515},"\"onLocationChanged: \"",[469,1474,1180],{"class":511},[469,1476,1477],{"class":489}," location);\n",[469,1479,1480,1483,1485,1488],{"class":471,"line":331},[469,1481,1482],{"class":475},"            if",[469,1484,1117],{"class":489},[469,1486,1487],{"class":660},"isBetterLocation",[469,1489,1490],{"class":489},"(location, mLocation)) {\n",[469,1492,1493,1496,1498,1501],{"class":471,"line":769},[469,1494,1495],{"class":479},"                mLocation",[469,1497,31],{"class":489},[469,1499,1500],{"class":660},"set",[469,1502,871],{"class":489},[469,1504,1505],{"class":471,"line":774},[469,1506,1507],{"class":489},"            }\n",[469,1509,1510],{"class":471,"line":796},[469,1511,525],{"emptyLinePlaceholder":320},[469,1513,1514,1517],{"class":471,"line":810},[469,1515,1516],{"class":660},"            callListener",[469,1518,1519],{"class":489},"(mLocation);\n",[469,1521,1522],{"class":471,"line":815},[469,1523,877],{"class":489},[469,1525,1526],{"class":471,"line":820},[469,1527,1528],{"class":489},"        ...\n",[469,1530,1531],{"class":471,"line":837},[469,1532,679],{"class":489},[12,1534,1535],{},"Enfin on a une méthode pour calculer les distances avec Android.:",[460,1537,1539],{"className":462,"code":1538,"language":464,"meta":291,"style":291},"    public static float getDistance(Location startLocation, Location lastLocation) {\n        float[] results = new float[1];\n        Location.distanceBetween(startLocation.getLatitude(), startLocation.getLongitude(), lastLocation.getLatitude(), lastLocation.getLongitude(), results);\n        float distance = results[0];\n        return distance;\n    }\n",[466,1540,1541,1566,1590,1640,1658,1668],{"__ignoreMap":291},[469,1542,1543,1545,1547,1549,1552,1554,1556,1558,1561,1563],{"class":471,"line":472},[469,1544,644],{"class":475},[469,1546,498],{"class":475},[469,1548,578],{"class":475},[469,1550,1551],{"class":660}," getDistance",[469,1553,664],{"class":507},[469,1555,667],{"class":479},[469,1557,671],{"class":507},[469,1559,1560],{"class":489},",",[469,1562,1420],{"class":479},[469,1564,1565],{"class":507}," lastLocation) {\n",[469,1567,1568,1571,1574,1576,1578,1580,1583,1585,1588],{"class":471,"line":292},[469,1569,1570],{"class":475},"        float",[469,1572,1573],{"class":507},"[] results ",[469,1575,512],{"class":511},[469,1577,624],{"class":475},[469,1579,578],{"class":475},[469,1581,1582],{"class":507},"[",[469,1584,424],{"class":541},[469,1586,1587],{"class":507},"]",[469,1589,519],{"class":489},[469,1591,1592,1595,1597,1600,1602,1605,1607,1610,1613,1615,1617,1620,1622,1625,1627,1629,1631,1633,1635,1637],{"class":471,"line":522},[469,1593,1594],{"class":479},"        Location",[469,1596,31],{"class":489},[469,1598,1599],{"class":660},"distanceBetween",[469,1601,664],{"class":489},[469,1603,1604],{"class":479},"startLocation",[469,1606,31],{"class":489},[469,1608,1609],{"class":660},"getLatitude",[469,1611,1612],{"class":489},"(), ",[469,1614,1604],{"class":479},[469,1616,31],{"class":489},[469,1618,1619],{"class":660},"getLongitude",[469,1621,1612],{"class":489},[469,1623,1624],{"class":479},"lastLocation",[469,1626,31],{"class":489},[469,1628,1609],{"class":660},[469,1630,1612],{"class":489},[469,1632,1624],{"class":479},[469,1634,31],{"class":489},[469,1636,1619],{"class":660},[469,1638,1639],{"class":489},"(), results);\n",[469,1641,1642,1644,1647,1649,1652,1654,1656],{"class":471,"line":528},[469,1643,1570],{"class":475},[469,1645,1646],{"class":507}," distance ",[469,1648,512],{"class":511},[469,1650,1651],{"class":507}," results[",[469,1653,1237],{"class":541},[469,1655,1587],{"class":507},[469,1657,519],{"class":489},[469,1659,1660,1663,1666],{"class":471,"line":547},[469,1661,1662],{"class":475},"        return",[469,1664,1665],{"class":507}," distance",[469,1667,519],{"class":489},[469,1669,1670],{"class":471,"line":569},[469,1671,679],{"class":507},[449,1673,1675],{"id":1674},"mainactivity","MainActivity",[12,1677,1678],{},"Retournons dans notre activité principale. Je passe la création du layout qui est fort simple.",[12,1680,1681],{},"Dans l'activité, nous allons commencer par implémenter la phase de création du cycle de vie de notre activité:",[460,1683,1685],{"className":462,"code":1684,"language":464,"meta":291,"style":291},"    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n",[466,1686,1687,1694,1711,1724],{"__ignoreMap":291},[469,1688,1689,1692],{"class":471,"line":472},[469,1690,1691],{"class":489},"    @",[469,1693,1439],{"class":479},[469,1695,1696,1698,1700,1703,1705,1708],{"class":471,"line":292},[469,1697,644],{"class":475},[469,1699,779],{"class":475},[469,1701,1702],{"class":660}," onCreate",[469,1704,664],{"class":507},[469,1706,1707],{"class":479},"Bundle",[469,1709,1710],{"class":507}," savedInstanceState) {\n",[469,1712,1713,1716,1718,1721],{"class":471,"line":522},[469,1714,1715],{"class":479},"        super",[469,1717,31],{"class":489},[469,1719,1720],{"class":660},"onCreate",[469,1722,1723],{"class":489},"(savedInstanceState);\n",[469,1725,1726,1729,1731,1734,1736,1739,1741,1744,1746],{"class":471,"line":528},[469,1727,1728],{"class":660},"        setContentView",[469,1730,664],{"class":507},[469,1732,1733],{"class":479},"R",[469,1735,31],{"class":489},[469,1737,1738],{"class":479},"layout",[469,1740,31],{"class":489},[469,1742,1743],{"class":479},"activity_main",[469,1745,1387],{"class":507},[469,1747,519],{"class":489},[12,1749,1750,1751,31],{},"Depuis Android 6.0, il faut demander à l'utilisateur la permission d'utiliser la position de l'utilisateur. Du coup\non commence par demander la permission à l'utilisateur d'avoir accès à sa position",[416,1752,1753],{},[47,1754,1758],{"href":1755,"ariaDescribedBy":1756,"dataFootnoteRef":291,"id":1757},"#user-content-fn-2",[422],"user-content-fnref-2","2",[460,1760,1762],{"className":462,"code":1761,"language":464,"meta":291,"style":291},"        requestPermissionsIfNecessary(new String[]{\n                Manifest.permission.ACCESS_FINE_LOCATION,\n        });\n",[466,1763,1764,1778,1796],{"__ignoreMap":291},[469,1765,1766,1769,1771,1774,1776],{"class":471,"line":472},[469,1767,1768],{"class":660},"        requestPermissionsIfNecessary",[469,1770,664],{"class":507},[469,1772,1773],{"class":475},"new",[469,1775,504],{"class":479},[469,1777,711],{"class":507},[469,1779,1780,1783,1785,1788,1790,1793],{"class":471,"line":292},[469,1781,1782],{"class":479},"                Manifest",[469,1784,31],{"class":489},[469,1786,1787],{"class":479},"permission",[469,1789,31],{"class":489},[469,1791,1792],{"class":479},"ACCESS_FINE_LOCATION",[469,1794,1795],{"class":489},",\n",[469,1797,1798,1801],{"class":471,"line":522},[469,1799,1800],{"class":507},"        })",[469,1802,519],{"class":489},[12,1804,1805],{},"Ensuite on appelle notre location manager et on écoute les changements de positions. Pour chaque changement de position\non met à jour la position et on met à jour les textes.",[460,1807,1809],{"className":462,"code":1808,"language":464,"meta":291,"style":291},"        locationManager = new ArroundLocationManager(this);\n        locationManager.addListener((location) -> {\n            if (!isMyServiceRunning(ArroundService.class)) {\n                startLocation = location;\n            }\n            MainActivity.this.updateTextLocation();\n        });\n        locationManager.start();\n",[466,1810,1811,1831,1849,1873,1883,1887,1903,1908],{"__ignoreMap":291},[469,1812,1813,1816,1818,1820,1822,1824,1827,1829],{"class":471,"line":472},[469,1814,1815],{"class":507},"        locationManager ",[469,1817,512],{"class":511},[469,1819,624],{"class":475},[469,1821,480],{"class":660},[469,1823,664],{"class":507},[469,1825,1826],{"class":479},"this",[469,1828,1387],{"class":507},[469,1830,519],{"class":489},[469,1832,1833,1836,1838,1841,1844,1847],{"class":471,"line":292},[469,1834,1835],{"class":479},"        locationManager",[469,1837,31],{"class":489},[469,1839,1840],{"class":660},"addListener",[469,1842,1843],{"class":489},"((location) ",[469,1845,1846],{"class":475},"->",[469,1848,490],{"class":489},[469,1850,1851,1853,1855,1858,1861,1863,1866,1868,1870],{"class":471,"line":522},[469,1852,1482],{"class":475},[469,1854,1117],{"class":489},[469,1856,1857],{"class":511},"!",[469,1859,1860],{"class":660},"isMyServiceRunning",[469,1862,664],{"class":489},[469,1864,1865],{"class":479},"ArroundService",[469,1867,31],{"class":489},[469,1869,476],{"class":479},[469,1871,1872],{"class":489},")) {\n",[469,1874,1875,1878,1880],{"class":471,"line":528},[469,1876,1877],{"class":489},"                startLocation ",[469,1879,512],{"class":511},[469,1881,1882],{"class":489}," location;\n",[469,1884,1885],{"class":471,"line":547},[469,1886,1507],{"class":489},[469,1888,1889,1892,1894,1896,1898,1901],{"class":471,"line":569},[469,1890,1891],{"class":479},"            MainActivity",[469,1893,31],{"class":489},[469,1895,1826],{"class":479},[469,1897,31],{"class":489},[469,1899,1900],{"class":660},"updateTextLocation",[469,1902,921],{"class":489},[469,1904,1905],{"class":471,"line":686},[469,1906,1907],{"class":489},"        });\n",[469,1909,1910,1912,1914,1917],{"class":471,"line":714},[469,1911,1835],{"class":479},[469,1913,31],{"class":489},[469,1915,1916],{"class":660},"start",[469,1918,921],{"class":489},[12,1920,1921],{},"Enfin on initialise l'IHM.",[460,1923,1925],{"className":462,"code":1924,"language":464,"meta":291,"style":291},"        distanceText = findViewById(R.id.distance);\n        startText = findViewById(R.id.start);\n        locationText = findViewById(R.id.location);\n\n        goButton = findViewById(R.id.goButton);\n        stopButton = findViewById(R.id.stopButton);\n        goButton.setOnClickListener(v -> {\n            setStart(true);\n        });\n        stopButton.setOnClickListener(v -> {\n            setStart(false);\n        });\n        setStart(isMyServiceRunning(ArroundService.class));\n        MainActivity.this.updateTextLocation();\n    }\n",[466,1926,1927,1955,1980,2006,2010,2036,2062,2079,2091,2095,2110,2121,2125,2147,2162],{"__ignoreMap":291},[469,1928,1929,1932,1934,1937,1939,1941,1943,1946,1948,1951,1953],{"class":471,"line":472},[469,1930,1931],{"class":507},"        distanceText ",[469,1933,512],{"class":511},[469,1935,1936],{"class":660}," findViewById",[469,1938,664],{"class":507},[469,1940,1733],{"class":479},[469,1942,31],{"class":489},[469,1944,1945],{"class":479},"id",[469,1947,31],{"class":489},[469,1949,1950],{"class":479},"distance",[469,1952,1387],{"class":507},[469,1954,519],{"class":489},[469,1956,1957,1960,1962,1964,1966,1968,1970,1972,1974,1976,1978],{"class":471,"line":292},[469,1958,1959],{"class":507},"        startText ",[469,1961,512],{"class":511},[469,1963,1936],{"class":660},[469,1965,664],{"class":507},[469,1967,1733],{"class":479},[469,1969,31],{"class":489},[469,1971,1945],{"class":479},[469,1973,31],{"class":489},[469,1975,1916],{"class":479},[469,1977,1387],{"class":507},[469,1979,519],{"class":489},[469,1981,1982,1985,1987,1989,1991,1993,1995,1997,1999,2002,2004],{"class":471,"line":522},[469,1983,1984],{"class":507},"        locationText ",[469,1986,512],{"class":511},[469,1988,1936],{"class":660},[469,1990,664],{"class":507},[469,1992,1733],{"class":479},[469,1994,31],{"class":489},[469,1996,1945],{"class":479},[469,1998,31],{"class":489},[469,2000,2001],{"class":479},"location",[469,2003,1387],{"class":507},[469,2005,519],{"class":489},[469,2007,2008],{"class":471,"line":528},[469,2009,525],{"emptyLinePlaceholder":320},[469,2011,2012,2015,2017,2019,2021,2023,2025,2027,2029,2032,2034],{"class":471,"line":547},[469,2013,2014],{"class":507},"        goButton ",[469,2016,512],{"class":511},[469,2018,1936],{"class":660},[469,2020,664],{"class":507},[469,2022,1733],{"class":479},[469,2024,31],{"class":489},[469,2026,1945],{"class":479},[469,2028,31],{"class":489},[469,2030,2031],{"class":479},"goButton",[469,2033,1387],{"class":507},[469,2035,519],{"class":489},[469,2037,2038,2041,2043,2045,2047,2049,2051,2053,2055,2058,2060],{"class":471,"line":569},[469,2039,2040],{"class":507},"        stopButton ",[469,2042,512],{"class":511},[469,2044,1936],{"class":660},[469,2046,664],{"class":507},[469,2048,1733],{"class":479},[469,2050,31],{"class":489},[469,2052,1945],{"class":479},[469,2054,31],{"class":489},[469,2056,2057],{"class":479},"stopButton",[469,2059,1387],{"class":507},[469,2061,519],{"class":489},[469,2063,2064,2067,2069,2072,2075,2077],{"class":471,"line":686},[469,2065,2066],{"class":479},"        goButton",[469,2068,31],{"class":489},[469,2070,2071],{"class":660},"setOnClickListener",[469,2073,2074],{"class":489},"(v ",[469,2076,1846],{"class":475},[469,2078,490],{"class":489},[469,2080,2081,2084,2086,2089],{"class":471,"line":714},[469,2082,2083],{"class":660},"            setStart",[469,2085,664],{"class":489},[469,2087,2088],{"class":541},"true",[469,2090,674],{"class":489},[469,2092,2093],{"class":471,"line":739},[469,2094,1907],{"class":489},[469,2096,2097,2100,2102,2104,2106,2108],{"class":471,"line":331},[469,2098,2099],{"class":479},"        stopButton",[469,2101,31],{"class":489},[469,2103,2071],{"class":660},[469,2105,2074],{"class":489},[469,2107,1846],{"class":475},[469,2109,490],{"class":489},[469,2111,2112,2114,2116,2119],{"class":471,"line":769},[469,2113,2083],{"class":660},[469,2115,664],{"class":489},[469,2117,2118],{"class":541},"false",[469,2120,674],{"class":489},[469,2122,2123],{"class":471,"line":774},[469,2124,1907],{"class":489},[469,2126,2127,2130,2132,2134,2136,2138,2140,2142,2145],{"class":471,"line":796},[469,2128,2129],{"class":660},"        setStart",[469,2131,664],{"class":507},[469,2133,1860],{"class":660},[469,2135,664],{"class":507},[469,2137,1865],{"class":479},[469,2139,31],{"class":489},[469,2141,476],{"class":479},[469,2143,2144],{"class":507},"))",[469,2146,519],{"class":489},[469,2148,2149,2152,2154,2156,2158,2160],{"class":471,"line":810},[469,2150,2151],{"class":479},"        MainActivity",[469,2153,31],{"class":489},[469,2155,1826],{"class":479},[469,2157,31],{"class":489},[469,2159,1900],{"class":660},[469,2161,921],{"class":489},[469,2163,2164],{"class":471,"line":815},[469,2165,679],{"class":507},[12,2167,2168],{},"Comme le LocationManager est un thread, nous devons faire attention à repasser dans le thread de l'UI afin de mettre\nà jour les labels :",[460,2170,2172],{"className":462,"code":2171,"language":464,"meta":291,"style":291},"    public void updateTextLocation() {\n        runOnUiThread(() -> {\n            if (this.startLocation != null && this.runLocation != null) {\n                distanceText.setText(getString(R.string.distanceLabel, (int) ArroundLocationManager.getDistance(startLocation, runLocation)));\n            } else {\n                distanceText.setText(\"\");\n            }\n            if (this.startLocation != null) {\n                startText.setText(getAddress(startLocation));\n            } else {\n                startText.setText(\"\");\n            }\n            if (this.runLocation != null) {\n                locationText.setText(getAddress(runLocation));\n            } else {\n                locationText.setText(\"\");\n            }\n        });\n    }\n",[466,2173,2174,2185,2197,2231,2279,2289,2304,2308,2326,2343,2351,2365,2369,2387,2403,2411,2425,2429,2435],{"__ignoreMap":291},[469,2175,2176,2178,2180,2183],{"class":471,"line":472},[469,2177,644],{"class":475},[469,2179,779],{"class":475},[469,2181,2182],{"class":660}," updateTextLocation",[469,2184,908],{"class":507},[469,2186,2187,2190,2193,2195],{"class":471,"line":292},[469,2188,2189],{"class":660},"        runOnUiThread",[469,2191,2192],{"class":507},"(() ",[469,2194,1846],{"class":475},[469,2196,490],{"class":507},[469,2198,2199,2201,2203,2205,2207,2209,2212,2214,2217,2220,2222,2225,2227,2229],{"class":471,"line":522},[469,2200,1482],{"class":475},[469,2202,1117],{"class":507},[469,2204,1826],{"class":479},[469,2206,31],{"class":489},[469,2208,1604],{"class":479},[469,2210,2211],{"class":511}," !=",[469,2213,542],{"class":541},[469,2215,2216],{"class":511}," &&",[469,2218,2219],{"class":479}," this",[469,2221,31],{"class":489},[469,2223,2224],{"class":479},"runLocation",[469,2226,2211],{"class":511},[469,2228,542],{"class":541},[469,2230,1126],{"class":507},[469,2232,2233,2236,2238,2241,2243,2246,2248,2250,2252,2255,2257,2260,2263,2266,2269,2271,2273,2276],{"class":471,"line":528},[469,2234,2235],{"class":479},"                distanceText",[469,2237,31],{"class":489},[469,2239,2240],{"class":660},"setText",[469,2242,664],{"class":489},[469,2244,2245],{"class":660},"getString",[469,2247,664],{"class":489},[469,2249,1733],{"class":479},[469,2251,31],{"class":489},[469,2253,2254],{"class":479},"string",[469,2256,31],{"class":489},[469,2258,2259],{"class":479},"distanceLabel",[469,2261,2262],{"class":489},", (",[469,2264,2265],{"class":475},"int",[469,2267,2268],{"class":489},") ",[469,2270,452],{"class":479},[469,2272,31],{"class":489},[469,2274,2275],{"class":660},"getDistance",[469,2277,2278],{"class":489},"(startLocation, runLocation)));\n",[469,2280,2281,2284,2287],{"class":471,"line":547},[469,2282,2283],{"class":507},"            } ",[469,2285,2286],{"class":475},"else",[469,2288,490],{"class":507},[469,2290,2291,2293,2295,2297,2299,2302],{"class":471,"line":569},[469,2292,2235],{"class":479},[469,2294,31],{"class":489},[469,2296,2240],{"class":660},[469,2298,664],{"class":489},[469,2300,2301],{"class":515},"\"\"",[469,2303,674],{"class":489},[469,2305,2306],{"class":471,"line":686},[469,2307,1507],{"class":507},[469,2309,2310,2312,2314,2316,2318,2320,2322,2324],{"class":471,"line":714},[469,2311,1482],{"class":475},[469,2313,1117],{"class":507},[469,2315,1826],{"class":479},[469,2317,31],{"class":489},[469,2319,1604],{"class":479},[469,2321,2211],{"class":511},[469,2323,542],{"class":541},[469,2325,1126],{"class":507},[469,2327,2328,2331,2333,2335,2337,2340],{"class":471,"line":739},[469,2329,2330],{"class":479},"                startText",[469,2332,31],{"class":489},[469,2334,2240],{"class":660},[469,2336,664],{"class":489},[469,2338,2339],{"class":660},"getAddress",[469,2341,2342],{"class":489},"(startLocation));\n",[469,2344,2345,2347,2349],{"class":471,"line":331},[469,2346,2283],{"class":507},[469,2348,2286],{"class":475},[469,2350,490],{"class":507},[469,2352,2353,2355,2357,2359,2361,2363],{"class":471,"line":769},[469,2354,2330],{"class":479},[469,2356,31],{"class":489},[469,2358,2240],{"class":660},[469,2360,664],{"class":489},[469,2362,2301],{"class":515},[469,2364,674],{"class":489},[469,2366,2367],{"class":471,"line":774},[469,2368,1507],{"class":507},[469,2370,2371,2373,2375,2377,2379,2381,2383,2385],{"class":471,"line":796},[469,2372,1482],{"class":475},[469,2374,1117],{"class":507},[469,2376,1826],{"class":479},[469,2378,31],{"class":489},[469,2380,2224],{"class":479},[469,2382,2211],{"class":511},[469,2384,542],{"class":541},[469,2386,1126],{"class":507},[469,2388,2389,2392,2394,2396,2398,2400],{"class":471,"line":810},[469,2390,2391],{"class":479},"                locationText",[469,2393,31],{"class":489},[469,2395,2240],{"class":660},[469,2397,664],{"class":489},[469,2399,2339],{"class":660},[469,2401,2402],{"class":489},"(runLocation));\n",[469,2404,2405,2407,2409],{"class":471,"line":815},[469,2406,2283],{"class":507},[469,2408,2286],{"class":475},[469,2410,490],{"class":507},[469,2412,2413,2415,2417,2419,2421,2423],{"class":471,"line":820},[469,2414,2391],{"class":479},[469,2416,31],{"class":489},[469,2418,2240],{"class":660},[469,2420,664],{"class":489},[469,2422,2301],{"class":515},[469,2424,674],{"class":489},[469,2426,2427],{"class":471,"line":837},[469,2428,1507],{"class":507},[469,2430,2431,2433],{"class":471,"line":860},[469,2432,1800],{"class":507},[469,2434,519],{"class":489},[469,2436,2437],{"class":471,"line":874},[469,2438,679],{"class":507},[12,2440,2441],{},"Pour ma part je ne connais pas la position GPS de ma maison par coeur, ni de là ou je me trouve. Cela tombe bien. Android\npropose une API pour geocoder une adresse. C'est à dire que l'on transforme une position en latitude, longitude en adresse\nlisible:",[460,2443,2445],{"className":462,"code":2444,"language":464,"meta":291,"style":291},"    String getAddress(Location location) {\n        Geocoder geocoder;\n        List\u003CAddress> addresses;\n\n        try {\n            geocoder = new Geocoder(this, Locale.getDefault());\n\n            addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1);\n\n            if (addresses.size() > 0 && addresses.get(0).getMaxAddressLineIndex() >= 0) {\n                return addresses.get(0).getAddressLine(0);\n            }\n            return \"Unknown\";\n        } catch (IOException e) {\n            return e.getMessage();\n        }\n    }\n",[466,2446,2447,2461,2471,2488,2492,2498,2530,2534,2570,2574,2624,2650,2654,2664,2680,2692,2696],{"__ignoreMap":291},[469,2448,2449,2452,2455,2457,2459],{"class":471,"line":472},[469,2450,2451],{"class":479},"    String",[469,2453,2454],{"class":660}," getAddress",[469,2456,664],{"class":507},[469,2458,667],{"class":479},[469,2460,834],{"class":507},[469,2462,2463,2466,2469],{"class":471,"line":292},[469,2464,2465],{"class":479},"        Geocoder",[469,2467,2468],{"class":507}," geocoder",[469,2470,519],{"class":489},[469,2472,2473,2476,2478,2481,2483,2486],{"class":471,"line":522},[469,2474,2475],{"class":479},"        List",[469,2477,606],{"class":489},[469,2479,2480],{"class":479},"Address",[469,2482,616],{"class":489},[469,2484,2485],{"class":507}," addresses",[469,2487,519],{"class":489},[469,2489,2490],{"class":471,"line":528},[469,2491,525],{"emptyLinePlaceholder":320},[469,2493,2494,2496],{"class":471,"line":547},[469,2495,1067],{"class":475},[469,2497,490],{"class":507},[469,2499,2500,2503,2505,2507,2510,2512,2514,2516,2519,2521,2524,2526,2528],{"class":471,"line":569},[469,2501,2502],{"class":507},"            geocoder ",[469,2504,512],{"class":511},[469,2506,624],{"class":475},[469,2508,2509],{"class":660}," Geocoder",[469,2511,664],{"class":507},[469,2513,1826],{"class":479},[469,2515,1560],{"class":489},[469,2517,2518],{"class":479}," Locale",[469,2520,31],{"class":489},[469,2522,2523],{"class":660},"getDefault",[469,2525,633],{"class":489},[469,2527,1387],{"class":507},[469,2529,519],{"class":489},[469,2531,2532],{"class":471,"line":686},[469,2533,525],{"emptyLinePlaceholder":320},[469,2535,2536,2539,2541,2543,2545,2548,2550,2552,2554,2556,2558,2560,2562,2564,2566,2568],{"class":471,"line":714},[469,2537,2538],{"class":507},"            addresses ",[469,2540,512],{"class":511},[469,2542,2468],{"class":479},[469,2544,31],{"class":489},[469,2546,2547],{"class":660},"getFromLocation",[469,2549,664],{"class":489},[469,2551,2001],{"class":479},[469,2553,31],{"class":489},[469,2555,1609],{"class":660},[469,2557,1612],{"class":489},[469,2559,2001],{"class":479},[469,2561,31],{"class":489},[469,2563,1619],{"class":660},[469,2565,1612],{"class":489},[469,2567,424],{"class":541},[469,2569,674],{"class":489},[469,2571,2572],{"class":471,"line":739},[469,2573,525],{"emptyLinePlaceholder":320},[469,2575,2576,2578,2580,2583,2585,2588,2590,2593,2596,2598,2600,2602,2605,2607,2609,2612,2615,2617,2620,2622],{"class":471,"line":331},[469,2577,1482],{"class":475},[469,2579,1117],{"class":507},[469,2581,2582],{"class":479},"addresses",[469,2584,31],{"class":489},[469,2586,2587],{"class":660},"size",[469,2589,633],{"class":489},[469,2591,2592],{"class":511}," >",[469,2594,2595],{"class":541}," 0",[469,2597,2216],{"class":511},[469,2599,2485],{"class":479},[469,2601,31],{"class":489},[469,2603,2604],{"class":660},"get",[469,2606,664],{"class":489},[469,2608,1237],{"class":541},[469,2610,2611],{"class":489},").",[469,2613,2614],{"class":660},"getMaxAddressLineIndex",[469,2616,633],{"class":489},[469,2618,2619],{"class":511}," >=",[469,2621,2595],{"class":541},[469,2623,1126],{"class":507},[469,2625,2626,2629,2631,2633,2635,2637,2639,2641,2644,2646,2648],{"class":471,"line":769},[469,2627,2628],{"class":475},"                return",[469,2630,2485],{"class":479},[469,2632,31],{"class":489},[469,2634,2604],{"class":660},[469,2636,664],{"class":489},[469,2638,1237],{"class":541},[469,2640,2611],{"class":489},[469,2642,2643],{"class":660},"getAddressLine",[469,2645,664],{"class":489},[469,2647,1237],{"class":541},[469,2649,674],{"class":489},[469,2651,2652],{"class":471,"line":774},[469,2653,1507],{"class":507},[469,2655,2656,2659,2662],{"class":471,"line":796},[469,2657,2658],{"class":475},"            return",[469,2660,2661],{"class":515}," \"Unknown\"",[469,2663,519],{"class":489},[469,2665,2666,2668,2670,2672,2675,2678],{"class":471,"line":810},[469,2667,1111],{"class":507},[469,2669,1114],{"class":475},[469,2671,1117],{"class":507},[469,2673,2674],{"class":479},"IOException",[469,2676,2677],{"class":670}," e",[469,2679,1126],{"class":507},[469,2681,2682,2684,2686,2688,2690],{"class":471,"line":815},[469,2683,2658],{"class":475},[469,2685,2677],{"class":479},[469,2687,31],{"class":489},[469,2689,1187],{"class":660},[469,2691,921],{"class":489},[469,2693,2694],{"class":471,"line":820},[469,2695,877],{"class":507},[469,2697,2698],{"class":471,"line":837},[469,2699,679],{"class":507},[12,2701,2702,2703,2705,2706,2708,2709],{},"@Override\npublic void onRequestPermissionsResult(int requestCode, String",[469,2704],{}," permissions, int",[469,2707],{}," grantResults) {\nArrayList",[2254,2710,2711,2712,2714,2715,2717],{}," permissionsToRequest = new ArrayList\u003C>();\nfor (int i = 0; i \u003C grantResults.length; i++) {\npermissionsToRequest.add(permissions",[469,2713,1137],{},");\n}\nif (permissionsToRequest.size() > 0) {\nActivityCompat.requestPermissions(this, permissionsToRequest.toArray(new String",[469,2716,1237],{},"), REQUEST_PERMISSIONS_REQUEST_CODE);\n}\n}",[12,2719,2720,2721,2723,2724],{},"private void requestPermissionsIfNecessary(String",[469,2722],{}," permissions) {\nArrayList",[2254,2725,2726,2727,2717],{}," permissionsToRequest = new ArrayList\u003C>();\nfor (String permission : permissions) {\nif (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {\n\u002F\u002F Permission is not granted\npermissionsToRequest.add(permission);\n}\n}\nif (permissionsToRequest.size() > 0) {\nActivityCompat.requestPermissions(this, permissionsToRequest.toArray(new String",[469,2728,1237],{},[33,2730,2732],{"id":2731},"le-service","Le service",[12,2734,2735,2736,2739],{},"Le service est démarré par l'application et doit ensuite survivre à la fermeture de l'application. Pour cela nous\nallons créer un ",[105,2737,2738],{},"foreground service"," qui, contrairement aux services en tâche de fond qui sont déclenchés sur un évènement avec\nune durée de vie relativement courte, va tourner au premier plan en affichant une notification.",[12,2741,2742,2743,458],{},"Pour démarrer le service depuis l'activité principale nous avons ajouté la méthode ",[466,2744,2745],{},"startServer",[460,2747,2749],{"className":462,"code":2748,"language":464,"meta":291,"style":291},"    private void startServer() {\n        if (!mBounded) {\n            Intent mIntent = new Intent(this, ArroundService.class);\n            mIntent.putExtra(\"startLocation\", startLocation);\n            ContextCompat.startForegroundService(this, mIntent);\n            bindService(mIntent, mConnection, BIND_AUTO_CREATE);\n        }\n    }\n",[466,2750,2751,2762,2774,2806,2824,2841,2861,2865],{"__ignoreMap":291},[469,2752,2753,2755,2757,2760],{"class":471,"line":472},[469,2754,495],{"class":475},[469,2756,779],{"class":475},[469,2758,2759],{"class":660}," startServer",[469,2761,908],{"class":507},[469,2763,2764,2767,2769,2771],{"class":471,"line":292},[469,2765,2766],{"class":475},"        if",[469,2768,1117],{"class":507},[469,2770,1857],{"class":511},[469,2772,2773],{"class":507},"mBounded) {\n",[469,2775,2776,2779,2782,2784,2786,2789,2791,2793,2795,2798,2800,2802,2804],{"class":471,"line":522},[469,2777,2778],{"class":479},"            Intent",[469,2780,2781],{"class":507}," mIntent ",[469,2783,512],{"class":511},[469,2785,624],{"class":475},[469,2787,2788],{"class":660}," Intent",[469,2790,664],{"class":507},[469,2792,1826],{"class":479},[469,2794,1560],{"class":489},[469,2796,2797],{"class":479}," ArroundService",[469,2799,31],{"class":489},[469,2801,476],{"class":479},[469,2803,1387],{"class":507},[469,2805,519],{"class":489},[469,2807,2808,2811,2813,2816,2818,2821],{"class":471,"line":528},[469,2809,2810],{"class":479},"            mIntent",[469,2812,31],{"class":489},[469,2814,2815],{"class":660},"putExtra",[469,2817,664],{"class":489},[469,2819,2820],{"class":515},"\"startLocation\"",[469,2822,2823],{"class":489},", startLocation);\n",[469,2825,2826,2829,2831,2834,2836,2838],{"class":471,"line":547},[469,2827,2828],{"class":479},"            ContextCompat",[469,2830,31],{"class":489},[469,2832,2833],{"class":660},"startForegroundService",[469,2835,664],{"class":489},[469,2837,1826],{"class":479},[469,2839,2840],{"class":489},", mIntent);\n",[469,2842,2843,2846,2849,2851,2854,2856,2859],{"class":471,"line":569},[469,2844,2845],{"class":660},"            bindService",[469,2847,2848],{"class":507},"(mIntent",[469,2850,1560],{"class":489},[469,2852,2853],{"class":507}," mConnection",[469,2855,1560],{"class":489},[469,2857,2858],{"class":507}," BIND_AUTO_CREATE)",[469,2860,519],{"class":489},[469,2862,2863],{"class":471,"line":686},[469,2864,877],{"class":507},[469,2866,2867],{"class":471,"line":714},[469,2868,679],{"class":507},[12,2870,2871,2872,2874,2875,2878,2879,2882],{},"Ce qui est important c'est la méthode ",[466,2873,2833],{}," dont le but est de démarrer le service en mode ",[105,2876,2877],{},"Foreground",".\nCette méthode a son pendant dans le service qui est ",[466,2880,2881],{},"startForeground",". Si cette dernière n'est pas appelée dans le service\nune erreur sera remontée par Android.",[12,2884,2885],{},"Du coup on implémente le service et on commence par l'initialisation :",[460,2887,2889],{"className":462,"code":2888,"language":464,"meta":291,"style":291},"    @Override\n    public void onCreate() {\n        super.onCreate();\n        notificationManager = new ArroundNotificationManager(this);\n        locationManager = new ArroundLocationManager(this);\n        locationManager.addListener(location -> {\n            runLocation = location;\n            callListener(location);\n            notificationManager.send(getDistance());\n            speakDistance();\n        });\n        locationManager.start();\n        textToSpeech = new TextToSpeech(this, this);\n    }\n",[466,2890,2891,2897,2907,2917,2937,2955,2970,2979,2985,3001,3008,3012,3022,3046],{"__ignoreMap":291},[469,2892,2893,2895],{"class":471,"line":472},[469,2894,1691],{"class":489},[469,2896,1439],{"class":479},[469,2898,2899,2901,2903,2905],{"class":471,"line":292},[469,2900,644],{"class":475},[469,2902,779],{"class":475},[469,2904,1702],{"class":660},[469,2906,908],{"class":507},[469,2908,2909,2911,2913,2915],{"class":471,"line":522},[469,2910,1715],{"class":479},[469,2912,31],{"class":489},[469,2914,1720],{"class":660},[469,2916,921],{"class":489},[469,2918,2919,2922,2924,2926,2929,2931,2933,2935],{"class":471,"line":528},[469,2920,2921],{"class":507},"        notificationManager ",[469,2923,512],{"class":511},[469,2925,624],{"class":475},[469,2927,2928],{"class":660}," ArroundNotificationManager",[469,2930,664],{"class":507},[469,2932,1826],{"class":479},[469,2934,1387],{"class":507},[469,2936,519],{"class":489},[469,2938,2939,2941,2943,2945,2947,2949,2951,2953],{"class":471,"line":547},[469,2940,1815],{"class":507},[469,2942,512],{"class":511},[469,2944,624],{"class":475},[469,2946,480],{"class":660},[469,2948,664],{"class":507},[469,2950,1826],{"class":479},[469,2952,1387],{"class":507},[469,2954,519],{"class":489},[469,2956,2957,2959,2961,2963,2966,2968],{"class":471,"line":569},[469,2958,1835],{"class":479},[469,2960,31],{"class":489},[469,2962,1840],{"class":660},[469,2964,2965],{"class":489},"(location ",[469,2967,1846],{"class":475},[469,2969,490],{"class":489},[469,2971,2972,2975,2977],{"class":471,"line":686},[469,2973,2974],{"class":489},"            runLocation ",[469,2976,512],{"class":511},[469,2978,1882],{"class":489},[469,2980,2981,2983],{"class":471,"line":714},[469,2982,1516],{"class":660},[469,2984,871],{"class":489},[469,2986,2987,2990,2992,2995,2997,2999],{"class":471,"line":739},[469,2988,2989],{"class":479},"            notificationManager",[469,2991,31],{"class":489},[469,2993,2994],{"class":660},"send",[469,2996,664],{"class":489},[469,2998,2275],{"class":660},[469,3000,1190],{"class":489},[469,3002,3003,3006],{"class":471,"line":331},[469,3004,3005],{"class":660},"            speakDistance",[469,3007,921],{"class":489},[469,3009,3010],{"class":471,"line":769},[469,3011,1907],{"class":489},[469,3013,3014,3016,3018,3020],{"class":471,"line":774},[469,3015,1835],{"class":479},[469,3017,31],{"class":489},[469,3019,1916],{"class":660},[469,3021,921],{"class":489},[469,3023,3024,3027,3029,3031,3034,3036,3038,3040,3042,3044],{"class":471,"line":796},[469,3025,3026],{"class":507},"        textToSpeech ",[469,3028,512],{"class":511},[469,3030,624],{"class":475},[469,3032,3033],{"class":660}," TextToSpeech",[469,3035,664],{"class":507},[469,3037,1826],{"class":479},[469,3039,1560],{"class":489},[469,3041,2219],{"class":479},[469,3043,1387],{"class":507},[469,3045,519],{"class":489},[469,3047,3048],{"class":471,"line":810},[469,3049,679],{"class":507},[12,3051,3052],{},"On démarre le notification manager qui a pour but de notifier l'utilisateur de l'existence d'un service qui tourne au\n1er plan. La notification est d'ailleurs nécessaire pour un service foreground.",[12,3054,3055,3056,3058],{},"On écoute aussi notre ",[466,3057,452],{}," qui lors des modifications s'occupe de mettre à jour la nouvelle position\net communique le changement de distance à l'utilisateur par voix et par notification.",[12,3060,3061,3062,3065],{},"On retrouve un ",[466,3063,3064],{},"callListener"," pour avertir l'utilisateur sur l'activité principale quand cette dernière est démarrée.",[12,3067,3068],{},"Ensuite le service démarre:",[460,3070,3072],{"className":462,"code":3071,"language":464,"meta":291,"style":291},"    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        Log.e(TAG, \"onStartCommand\");\n\n        PowerManager powerService = (PowerManager) getSystemService(Context.POWER_SERVICE);\n        wakeLock = powerService.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, \"ArroundService::lock\");\n        wakeLock.acquire();\n\n        Bundle extras = intent.getExtras();\n        Location location = (Location) extras.get(\"startLocation\");\n        if (location != null) {\n            this.startLocation = location;\n        }\n\n        Notification notification = notificationManager.notifyDistance(0);\n        startForeground(ArroundNotificationManager.ARROUND_ID, notification);\n\n        return START_STICKY;\n    }\n",[466,3073,3074,3080,3111,3127,3131,3161,3194,3206,3210,3229,3254,3268,3284,3288,3292,3316,3338,3342,3351],{"__ignoreMap":291},[469,3075,3076,3078],{"class":471,"line":472},[469,3077,1691],{"class":489},[469,3079,1439],{"class":479},[469,3081,3082,3084,3086,3089,3091,3094,3097,3099,3101,3104,3106,3108],{"class":471,"line":292},[469,3083,644],{"class":475},[469,3085,556],{"class":475},[469,3087,3088],{"class":660}," onStartCommand",[469,3090,664],{"class":507},[469,3092,3093],{"class":479},"Intent",[469,3095,3096],{"class":507}," intent",[469,3098,1560],{"class":489},[469,3100,556],{"class":475},[469,3102,3103],{"class":507}," flags",[469,3105,1560],{"class":489},[469,3107,556],{"class":475},[469,3109,3110],{"class":507}," startId) {\n",[469,3112,3113,3116,3118,3120,3122,3125],{"class":471,"line":522},[469,3114,3115],{"class":479},"        Log",[469,3117,31],{"class":489},[469,3119,1398],{"class":660},[469,3121,1140],{"class":489},[469,3123,3124],{"class":515},"\"onStartCommand\"",[469,3126,674],{"class":489},[469,3128,3129],{"class":471,"line":528},[469,3130,525],{"emptyLinePlaceholder":320},[469,3132,3133,3136,3139,3141,3144,3147,3149,3152,3154,3157,3159],{"class":471,"line":547},[469,3134,3135],{"class":479},"        PowerManager",[469,3137,3138],{"class":507}," powerService ",[469,3140,512],{"class":511},[469,3142,3143],{"class":507}," (PowerManager) ",[469,3145,3146],{"class":660},"getSystemService",[469,3148,664],{"class":507},[469,3150,3151],{"class":479},"Context",[469,3153,31],{"class":489},[469,3155,3156],{"class":479},"POWER_SERVICE",[469,3158,1387],{"class":507},[469,3160,519],{"class":489},[469,3162,3163,3166,3168,3171,3173,3176,3178,3181,3183,3186,3189,3192],{"class":471,"line":569},[469,3164,3165],{"class":507},"        wakeLock ",[469,3167,512],{"class":511},[469,3169,3170],{"class":479}," powerService",[469,3172,31],{"class":489},[469,3174,3175],{"class":660},"newWakeLock",[469,3177,664],{"class":489},[469,3179,3180],{"class":479},"PowerManager",[469,3182,31],{"class":489},[469,3184,3185],{"class":479},"PARTIAL_WAKE_LOCK",[469,3187,3188],{"class":489},", ",[469,3190,3191],{"class":515},"\"ArroundService::lock\"",[469,3193,674],{"class":489},[469,3195,3196,3199,3201,3204],{"class":471,"line":686},[469,3197,3198],{"class":479},"        wakeLock",[469,3200,31],{"class":489},[469,3202,3203],{"class":660},"acquire",[469,3205,921],{"class":489},[469,3207,3208],{"class":471,"line":714},[469,3209,525],{"emptyLinePlaceholder":320},[469,3211,3212,3215,3218,3220,3222,3224,3227],{"class":471,"line":739},[469,3213,3214],{"class":479},"        Bundle",[469,3216,3217],{"class":507}," extras ",[469,3219,512],{"class":511},[469,3221,3096],{"class":479},[469,3223,31],{"class":489},[469,3225,3226],{"class":660},"getExtras",[469,3228,921],{"class":489},[469,3230,3231,3233,3236,3238,3241,3244,3246,3248,3250,3252],{"class":471,"line":331},[469,3232,1594],{"class":479},[469,3234,3235],{"class":507}," location ",[469,3237,512],{"class":511},[469,3239,3240],{"class":507}," (Location) ",[469,3242,3243],{"class":479},"extras",[469,3245,31],{"class":489},[469,3247,2604],{"class":660},[469,3249,664],{"class":489},[469,3251,2820],{"class":515},[469,3253,674],{"class":489},[469,3255,3256,3258,3261,3264,3266],{"class":471,"line":769},[469,3257,2766],{"class":475},[469,3259,3260],{"class":507}," (location ",[469,3262,3263],{"class":511},"!=",[469,3265,542],{"class":541},[469,3267,1126],{"class":507},[469,3269,3270,3273,3275,3277,3280,3282],{"class":471,"line":774},[469,3271,3272],{"class":479},"            this",[469,3274,31],{"class":489},[469,3276,1604],{"class":479},[469,3278,3279],{"class":511}," =",[469,3281,1455],{"class":507},[469,3283,519],{"class":489},[469,3285,3286],{"class":471,"line":796},[469,3287,877],{"class":507},[469,3289,3290],{"class":471,"line":810},[469,3291,525],{"emptyLinePlaceholder":320},[469,3293,3294,3297,3300,3302,3305,3307,3310,3312,3314],{"class":471,"line":815},[469,3295,3296],{"class":479},"        Notification",[469,3298,3299],{"class":507}," notification ",[469,3301,512],{"class":511},[469,3303,3304],{"class":479}," notificationManager",[469,3306,31],{"class":489},[469,3308,3309],{"class":660},"notifyDistance",[469,3311,664],{"class":489},[469,3313,1237],{"class":541},[469,3315,674],{"class":489},[469,3317,3318,3321,3323,3326,3328,3331,3333,3336],{"class":471,"line":820},[469,3319,3320],{"class":660},"        startForeground",[469,3322,664],{"class":507},[469,3324,3325],{"class":479},"ArroundNotificationManager",[469,3327,31],{"class":489},[469,3329,3330],{"class":479},"ARROUND_ID",[469,3332,1560],{"class":489},[469,3334,3335],{"class":507}," notification)",[469,3337,519],{"class":489},[469,3339,3340],{"class":471,"line":837},[469,3341,525],{"emptyLinePlaceholder":320},[469,3343,3344,3346,3349],{"class":471,"line":860},[469,3345,1662],{"class":475},[469,3347,3348],{"class":507}," START_STICKY",[469,3350,519],{"class":489},[469,3352,3353],{"class":471,"line":874},[469,3354,679],{"class":507},[12,3356,3357],{},"Afin qu'Android et son système de gestion d'nergie ne tuent pas notre service pendant que l'on court, nous commençons\npar mettre un PARTIAL_WAKE_LOCK. Ensuite nous récupérons la position (sauf si nous sommes issus d'un redémarrage de\nl'application) et appelons la méthode startForeground avec une notification persistante que nous avons créé.",[12,3359,3360],{},"Surtout après l'acquision du Wake Lock, il est important de le relâcher lors de la fermeture (quand l'utilisateur\nclique sur stop):",[460,3362,3364],{"className":462,"code":3363,"language":464,"meta":291,"style":291},"    public void stop() {\n        if (wakeLock != null) {\n            if (wakeLock.isHeld()) {\n                wakeLock.release();\n                wakeLock = null;\n            }\n        }\n        stopForeground(true);\n        stopSelf();\n    }\n",[466,3365,3366,3377,3390,3408,3420,3431,3435,3439,3452,3461],{"__ignoreMap":291},[469,3367,3368,3370,3372,3375],{"class":471,"line":472},[469,3369,644],{"class":475},[469,3371,779],{"class":475},[469,3373,3374],{"class":660}," stop",[469,3376,908],{"class":507},[469,3378,3379,3381,3384,3386,3388],{"class":471,"line":292},[469,3380,2766],{"class":475},[469,3382,3383],{"class":507}," (wakeLock ",[469,3385,3263],{"class":511},[469,3387,542],{"class":541},[469,3389,1126],{"class":507},[469,3391,3392,3394,3396,3399,3401,3404,3406],{"class":471,"line":522},[469,3393,1482],{"class":475},[469,3395,1117],{"class":507},[469,3397,3398],{"class":479},"wakeLock",[469,3400,31],{"class":489},[469,3402,3403],{"class":660},"isHeld",[469,3405,633],{"class":489},[469,3407,1126],{"class":507},[469,3409,3410,3413,3415,3418],{"class":471,"line":528},[469,3411,3412],{"class":479},"                wakeLock",[469,3414,31],{"class":489},[469,3416,3417],{"class":660},"release",[469,3419,921],{"class":489},[469,3421,3422,3425,3427,3429],{"class":471,"line":547},[469,3423,3424],{"class":507},"                wakeLock ",[469,3426,512],{"class":511},[469,3428,542],{"class":541},[469,3430,519],{"class":489},[469,3432,3433],{"class":471,"line":569},[469,3434,1507],{"class":507},[469,3436,3437],{"class":471,"line":686},[469,3438,877],{"class":507},[469,3440,3441,3444,3446,3448,3450],{"class":471,"line":714},[469,3442,3443],{"class":660},"        stopForeground",[469,3445,664],{"class":507},[469,3447,2088],{"class":541},[469,3449,1387],{"class":507},[469,3451,519],{"class":489},[469,3453,3454,3457,3459],{"class":471,"line":739},[469,3455,3456],{"class":660},"        stopSelf",[469,3458,633],{"class":507},[469,3460,519],{"class":489},[469,3462,3463],{"class":471,"line":331},[469,3464,679],{"class":507},[12,3466,3467,3468,3471,3472,2611],{},"On en profite pour arrêter la notification (avec ̀",[466,3469,3470],{},"stopForeground",") et arrêter le service (avec ",[466,3473,3474],{},"stopSelf",[12,3476,3477,3478,3481,3482,458],{},"Pour lire à l'utilisateur la distance, nous utilisons ",[466,3479,3480],{},"android.speech.tts.TextToSpeech",". Son utilisation\nest fort simple et se fait lors de l'appel à ",[466,3483,3484],{},"speakDistance",[460,3486,3488],{"className":462,"code":3487,"language":464,"meta":291,"style":291},"    private void speakDistance() {\n        int distance = (int) getDistance();\n        if (Math.abs(lastDistance - distance) > 100) {\n            int stringId;\n            if (distance > 1000) {\n                stringId = R.string.speaker_meters_alert;\n            } else if (distance > 900) {\n                stringId = R.string.speaker_meters_warn;\n            } else {\n                stringId = R.string.speaker_meters_info;\n            }\n\n            String text = getString(stringId, distance);\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null, null);\n            } else {\n                textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null);\n            }\n\n            lastDistance = distance;\n        }\n    }\n",[466,3489,3490,3501,3522,3552,3562,3575,3596,3614,3633,3641,3660,3664,3668,3690,3694,3730,3762,3770,3792,3796,3800,3811,3815],{"__ignoreMap":291},[469,3491,3492,3494,3496,3499],{"class":471,"line":472},[469,3493,495],{"class":475},[469,3495,779],{"class":475},[469,3497,3498],{"class":660}," speakDistance",[469,3500,908],{"class":507},[469,3502,3503,3506,3508,3510,3512,3514,3516,3518,3520],{"class":471,"line":292},[469,3504,3505],{"class":475},"        int",[469,3507,1646],{"class":507},[469,3509,512],{"class":511},[469,3511,1117],{"class":507},[469,3513,2265],{"class":475},[469,3515,2268],{"class":507},[469,3517,2275],{"class":660},[469,3519,633],{"class":507},[469,3521,519],{"class":489},[469,3523,3524,3526,3528,3531,3533,3536,3539,3542,3545,3547,3550],{"class":471,"line":522},[469,3525,2766],{"class":475},[469,3527,1117],{"class":507},[469,3529,3530],{"class":479},"Math",[469,3532,31],{"class":489},[469,3534,3535],{"class":660},"abs",[469,3537,3538],{"class":489},"(lastDistance ",[469,3540,3541],{"class":511},"-",[469,3543,3544],{"class":489}," distance)",[469,3546,2592],{"class":511},[469,3548,3549],{"class":541}," 100",[469,3551,1126],{"class":507},[469,3553,3554,3557,3560],{"class":471,"line":528},[469,3555,3556],{"class":475},"            int",[469,3558,3559],{"class":507}," stringId",[469,3561,519],{"class":489},[469,3563,3564,3566,3569,3571,3573],{"class":471,"line":547},[469,3565,1482],{"class":475},[469,3567,3568],{"class":507}," (distance ",[469,3570,616],{"class":511},[469,3572,564],{"class":541},[469,3574,1126],{"class":507},[469,3576,3577,3580,3582,3585,3587,3589,3591,3594],{"class":471,"line":569},[469,3578,3579],{"class":507},"                stringId ",[469,3581,512],{"class":511},[469,3583,3584],{"class":479}," R",[469,3586,31],{"class":489},[469,3588,2254],{"class":479},[469,3590,31],{"class":489},[469,3592,3593],{"class":479},"speaker_meters_alert",[469,3595,519],{"class":489},[469,3597,3598,3600,3602,3605,3607,3609,3612],{"class":471,"line":686},[469,3599,2283],{"class":507},[469,3601,2286],{"class":475},[469,3603,3604],{"class":475}," if",[469,3606,3568],{"class":507},[469,3608,616],{"class":511},[469,3610,3611],{"class":541}," 900",[469,3613,1126],{"class":507},[469,3615,3616,3618,3620,3622,3624,3626,3628,3631],{"class":471,"line":714},[469,3617,3579],{"class":507},[469,3619,512],{"class":511},[469,3621,3584],{"class":479},[469,3623,31],{"class":489},[469,3625,2254],{"class":479},[469,3627,31],{"class":489},[469,3629,3630],{"class":479},"speaker_meters_warn",[469,3632,519],{"class":489},[469,3634,3635,3637,3639],{"class":471,"line":739},[469,3636,2283],{"class":507},[469,3638,2286],{"class":475},[469,3640,490],{"class":507},[469,3642,3643,3645,3647,3649,3651,3653,3655,3658],{"class":471,"line":331},[469,3644,3579],{"class":507},[469,3646,512],{"class":511},[469,3648,3584],{"class":479},[469,3650,31],{"class":489},[469,3652,2254],{"class":479},[469,3654,31],{"class":489},[469,3656,3657],{"class":479},"speaker_meters_info",[469,3659,519],{"class":489},[469,3661,3662],{"class":471,"line":769},[469,3663,1507],{"class":507},[469,3665,3666],{"class":471,"line":774},[469,3667,525],{"emptyLinePlaceholder":320},[469,3669,3670,3673,3676,3678,3681,3684,3686,3688],{"class":471,"line":796},[469,3671,3672],{"class":479},"            String",[469,3674,3675],{"class":507}," text ",[469,3677,512],{"class":511},[469,3679,3680],{"class":660}," getString",[469,3682,3683],{"class":507},"(stringId",[469,3685,1560],{"class":489},[469,3687,3544],{"class":507},[469,3689,519],{"class":489},[469,3691,3692],{"class":471,"line":810},[469,3693,525],{"emptyLinePlaceholder":320},[469,3695,3696,3698,3700,3703,3705,3708,3710,3713,3715,3718,3720,3723,3725,3728],{"class":471,"line":815},[469,3697,1482],{"class":475},[469,3699,1117],{"class":507},[469,3701,3702],{"class":479},"Build",[469,3704,31],{"class":489},[469,3706,3707],{"class":479},"VERSION",[469,3709,31],{"class":489},[469,3711,3712],{"class":479},"SDK_INT",[469,3714,2619],{"class":511},[469,3716,3717],{"class":479}," Build",[469,3719,31],{"class":489},[469,3721,3722],{"class":479},"VERSION_CODES",[469,3724,31],{"class":489},[469,3726,3727],{"class":479},"LOLLIPOP",[469,3729,1126],{"class":507},[469,3731,3732,3735,3737,3740,3743,3746,3748,3751,3753,3756,3758,3760],{"class":471,"line":820},[469,3733,3734],{"class":479},"                textToSpeech",[469,3736,31],{"class":489},[469,3738,3739],{"class":660},"speak",[469,3741,3742],{"class":489},"(text, ",[469,3744,3745],{"class":479},"TextToSpeech",[469,3747,31],{"class":489},[469,3749,3750],{"class":479},"QUEUE_FLUSH",[469,3752,3188],{"class":489},[469,3754,3755],{"class":541},"null",[469,3757,3188],{"class":489},[469,3759,3755],{"class":541},[469,3761,674],{"class":489},[469,3763,3764,3766,3768],{"class":471,"line":837},[469,3765,2283],{"class":507},[469,3767,2286],{"class":475},[469,3769,490],{"class":507},[469,3771,3772,3774,3776,3778,3780,3782,3784,3786,3788,3790],{"class":471,"line":860},[469,3773,3734],{"class":479},[469,3775,31],{"class":489},[469,3777,3739],{"class":660},[469,3779,3742],{"class":489},[469,3781,3745],{"class":479},[469,3783,31],{"class":489},[469,3785,3750],{"class":479},[469,3787,3188],{"class":489},[469,3789,3755],{"class":541},[469,3791,674],{"class":489},[469,3793,3794],{"class":471,"line":874},[469,3795,1507],{"class":507},[469,3797,3798],{"class":471,"line":880},[469,3799,525],{"emptyLinePlaceholder":320},[469,3801,3802,3805,3807,3809],{"class":471,"line":1097},[469,3803,3804],{"class":507},"            lastDistance ",[469,3806,512],{"class":511},[469,3808,1665],{"class":507},[469,3810,519],{"class":489},[469,3812,3813],{"class":471,"line":1108},[469,3814,877],{"class":507},[469,3816,3817],{"class":471,"line":1129},[469,3818,679],{"class":507},[12,3820,3821],{},"Enfin le dernier point concerne la création des notifications. Depuis la version Android O, l'application doit\nassocier une notification à un channel. Cela permet à Android de présenter à l'utilisateur une interface avec les\nnotifications possibles et de pouvoir désactiver\u002Factiver ces dernières au cas par cas.",[12,3823,3824,3827],{},[466,3825,3826],{},"AndroidNotificationManager"," est là pour ce but. Il va créer le channel de notification et notifier la distance à\nl'utilisateur.",[460,3829,3831],{"className":462,"code":3830,"language":464,"meta":291,"style":291},"    private static final String CHANNEL_DEFAULT_IMPORTANCE = \"Running\";\n    public static final int ARROUND_ID = 1;\n\n    private void createNotificationChannel() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            CharSequence name = context.getString(R.string.channel_name);\n            String description = context.getString(R.string.channel_description);\n            NotificationChannel channel = new NotificationChannel(CHANNEL_DEFAULT_IMPORTANCE, name, NotificationManager.IMPORTANCE_DEFAULT);\n            channel.setDescription(description);\n            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);\n            notificationManager.createNotificationChannel(channel);\n        }\n    }\n\n    public Notification createNotification(float distance) {\n        Intent notificationIntent = new Intent(context, MainActivity.class);\n        PendingIntent pendingIntent =\n                PendingIntent.getActivity(context, 0, notificationIntent, 0);\n\n        return new NotificationCompat.Builder(context, CHANNEL_DEFAULT_IMPORTANCE)\n                .setContentTitle(context.getText(R.string.notification_title))\n                .setContentText(context.getString(R.string.notification_message, (int) distance))\n                .setSmallIcon(android.R.drawable.ic_menu_mylocation)\n                .setContentIntent(pendingIntent)\n                .build();\n    }\n\n    public void notifyDistance(float distance) {\n        Notification n = this.createNotification(distance);\n        NotificationManager notificationManager = context.getSystemService(NotificationManager.class);\n        notificationManager.notify(ARROUND_ID, n);\n    }\n",[466,3832,3833,3853,3873,3877,3888,3919,3951,3981,4018,4031,4058,4070,4074,4078,4082,4100,4130,4141,4163,4167,4184,4218,4253,4280,4290,4299,4303,4307,4322,4341,4366,4379],{"__ignoreMap":291},[469,3834,3835,3837,3839,3841,3843,3846,3848,3851],{"class":471,"line":472},[469,3836,495],{"class":475},[469,3838,498],{"class":475},[469,3840,501],{"class":475},[469,3842,504],{"class":479},[469,3844,3845],{"class":507}," CHANNEL_DEFAULT_IMPORTANCE ",[469,3847,512],{"class":511},[469,3849,3850],{"class":515}," \"Running\"",[469,3852,519],{"class":489},[469,3854,3855,3857,3859,3861,3863,3866,3868,3871],{"class":471,"line":292},[469,3856,644],{"class":475},[469,3858,498],{"class":475},[469,3860,501],{"class":475},[469,3862,556],{"class":475},[469,3864,3865],{"class":507}," ARROUND_ID ",[469,3867,512],{"class":511},[469,3869,3870],{"class":541}," 1",[469,3872,519],{"class":489},[469,3874,3875],{"class":471,"line":522},[469,3876,525],{"emptyLinePlaceholder":320},[469,3878,3879,3881,3883,3886],{"class":471,"line":528},[469,3880,495],{"class":475},[469,3882,779],{"class":475},[469,3884,3885],{"class":660}," createNotificationChannel",[469,3887,908],{"class":507},[469,3889,3890,3892,3894,3896,3898,3900,3902,3904,3906,3908,3910,3912,3914,3917],{"class":471,"line":547},[469,3891,2766],{"class":475},[469,3893,1117],{"class":507},[469,3895,3702],{"class":479},[469,3897,31],{"class":489},[469,3899,3707],{"class":479},[469,3901,31],{"class":489},[469,3903,3712],{"class":479},[469,3905,2619],{"class":511},[469,3907,3717],{"class":479},[469,3909,31],{"class":489},[469,3911,3722],{"class":479},[469,3913,31],{"class":489},[469,3915,3916],{"class":479},"O",[469,3918,1126],{"class":507},[469,3920,3921,3924,3927,3929,3932,3934,3936,3938,3940,3942,3944,3946,3949],{"class":471,"line":569},[469,3922,3923],{"class":479},"            CharSequence",[469,3925,3926],{"class":507}," name ",[469,3928,512],{"class":511},[469,3930,3931],{"class":479}," context",[469,3933,31],{"class":489},[469,3935,2245],{"class":660},[469,3937,664],{"class":489},[469,3939,1733],{"class":479},[469,3941,31],{"class":489},[469,3943,2254],{"class":479},[469,3945,31],{"class":489},[469,3947,3948],{"class":479},"channel_name",[469,3950,674],{"class":489},[469,3952,3953,3955,3958,3960,3962,3964,3966,3968,3970,3972,3974,3976,3979],{"class":471,"line":686},[469,3954,3672],{"class":479},[469,3956,3957],{"class":507}," description ",[469,3959,512],{"class":511},[469,3961,3931],{"class":479},[469,3963,31],{"class":489},[469,3965,2245],{"class":660},[469,3967,664],{"class":489},[469,3969,1733],{"class":479},[469,3971,31],{"class":489},[469,3973,2254],{"class":479},[469,3975,31],{"class":489},[469,3977,3978],{"class":479},"channel_description",[469,3980,674],{"class":489},[469,3982,3983,3986,3989,3991,3993,3996,3999,4001,4004,4006,4009,4011,4014,4016],{"class":471,"line":714},[469,3984,3985],{"class":479},"            NotificationChannel",[469,3987,3988],{"class":507}," channel ",[469,3990,512],{"class":511},[469,3992,624],{"class":475},[469,3994,3995],{"class":660}," NotificationChannel",[469,3997,3998],{"class":507},"(CHANNEL_DEFAULT_IMPORTANCE",[469,4000,1560],{"class":489},[469,4002,4003],{"class":507}," name",[469,4005,1560],{"class":489},[469,4007,4008],{"class":479}," NotificationManager",[469,4010,31],{"class":489},[469,4012,4013],{"class":479},"IMPORTANCE_DEFAULT",[469,4015,1387],{"class":507},[469,4017,519],{"class":489},[469,4019,4020,4023,4025,4028],{"class":471,"line":739},[469,4021,4022],{"class":479},"            channel",[469,4024,31],{"class":489},[469,4026,4027],{"class":660},"setDescription",[469,4029,4030],{"class":489},"(description);\n",[469,4032,4033,4036,4039,4041,4043,4045,4047,4049,4052,4054,4056],{"class":471,"line":331},[469,4034,4035],{"class":479},"            NotificationManager",[469,4037,4038],{"class":507}," notificationManager ",[469,4040,512],{"class":511},[469,4042,3931],{"class":479},[469,4044,31],{"class":489},[469,4046,3146],{"class":660},[469,4048,664],{"class":489},[469,4050,4051],{"class":479},"NotificationManager",[469,4053,31],{"class":489},[469,4055,476],{"class":479},[469,4057,674],{"class":489},[469,4059,4060,4062,4064,4067],{"class":471,"line":769},[469,4061,2989],{"class":479},[469,4063,31],{"class":489},[469,4065,4066],{"class":660},"createNotificationChannel",[469,4068,4069],{"class":489},"(channel);\n",[469,4071,4072],{"class":471,"line":774},[469,4073,877],{"class":507},[469,4075,4076],{"class":471,"line":796},[469,4077,679],{"class":507},[469,4079,4080],{"class":471,"line":810},[469,4081,525],{"emptyLinePlaceholder":320},[469,4083,4084,4086,4089,4092,4094,4097],{"class":471,"line":815},[469,4085,644],{"class":475},[469,4087,4088],{"class":479}," Notification",[469,4090,4091],{"class":660}," createNotification",[469,4093,664],{"class":507},[469,4095,4096],{"class":475},"float",[469,4098,4099],{"class":507}," distance) {\n",[469,4101,4102,4105,4108,4110,4112,4114,4117,4119,4122,4124,4126,4128],{"class":471,"line":820},[469,4103,4104],{"class":479},"        Intent",[469,4106,4107],{"class":507}," notificationIntent ",[469,4109,512],{"class":511},[469,4111,624],{"class":475},[469,4113,2788],{"class":660},[469,4115,4116],{"class":507},"(context",[469,4118,1560],{"class":489},[469,4120,4121],{"class":479}," MainActivity",[469,4123,31],{"class":489},[469,4125,476],{"class":479},[469,4127,1387],{"class":507},[469,4129,519],{"class":489},[469,4131,4132,4135,4138],{"class":471,"line":837},[469,4133,4134],{"class":479},"        PendingIntent",[469,4136,4137],{"class":507}," pendingIntent ",[469,4139,4140],{"class":511},"=\n",[469,4142,4143,4146,4148,4151,4154,4156,4159,4161],{"class":471,"line":860},[469,4144,4145],{"class":479},"                PendingIntent",[469,4147,31],{"class":489},[469,4149,4150],{"class":660},"getActivity",[469,4152,4153],{"class":489},"(context, ",[469,4155,1237],{"class":541},[469,4157,4158],{"class":489},", notificationIntent, ",[469,4160,1237],{"class":541},[469,4162,674],{"class":489},[469,4164,4165],{"class":471,"line":874},[469,4166,525],{"emptyLinePlaceholder":320},[469,4168,4169,4171,4173,4176,4178,4181],{"class":471,"line":880},[469,4170,1662],{"class":475},[469,4172,624],{"class":475},[469,4174,4175],{"class":507}," NotificationCompat",[469,4177,31],{"class":489},[469,4179,4180],{"class":660},"Builder",[469,4182,4183],{"class":489},"(context, CHANNEL_DEFAULT_IMPORTANCE)\n",[469,4185,4186,4189,4192,4194,4197,4199,4202,4204,4206,4208,4210,4212,4215],{"class":471,"line":1097},[469,4187,4188],{"class":489},"                .",[469,4190,4191],{"class":660},"setContentTitle",[469,4193,664],{"class":489},[469,4195,4196],{"class":479},"context",[469,4198,31],{"class":489},[469,4200,4201],{"class":660},"getText",[469,4203,664],{"class":489},[469,4205,1733],{"class":479},[469,4207,31],{"class":489},[469,4209,2254],{"class":479},[469,4211,31],{"class":489},[469,4213,4214],{"class":479},"notification_title",[469,4216,4217],{"class":489},"))\n",[469,4219,4220,4222,4225,4227,4229,4231,4233,4235,4237,4239,4241,4243,4246,4248,4250],{"class":471,"line":1108},[469,4221,4188],{"class":489},[469,4223,4224],{"class":660},"setContentText",[469,4226,664],{"class":489},[469,4228,4196],{"class":479},[469,4230,31],{"class":489},[469,4232,2245],{"class":660},[469,4234,664],{"class":489},[469,4236,1733],{"class":479},[469,4238,31],{"class":489},[469,4240,2254],{"class":479},[469,4242,31],{"class":489},[469,4244,4245],{"class":479},"notification_message",[469,4247,2262],{"class":489},[469,4249,2265],{"class":475},[469,4251,4252],{"class":489},") distance))\n",[469,4254,4255,4257,4260,4262,4264,4266,4268,4270,4273,4275,4278],{"class":471,"line":1129},[469,4256,4188],{"class":489},[469,4258,4259],{"class":660},"setSmallIcon",[469,4261,664],{"class":489},[469,4263,330],{"class":479},[469,4265,31],{"class":489},[469,4267,1733],{"class":479},[469,4269,31],{"class":489},[469,4271,4272],{"class":479},"drawable",[469,4274,31],{"class":489},[469,4276,4277],{"class":479},"ic_menu_mylocation",[469,4279,759],{"class":489},[469,4281,4282,4284,4287],{"class":471,"line":1149},[469,4283,4188],{"class":489},[469,4285,4286],{"class":660},"setContentIntent",[469,4288,4289],{"class":489},"(pendingIntent)\n",[469,4291,4292,4294,4297],{"class":471,"line":1165},[469,4293,4188],{"class":489},[469,4295,4296],{"class":660},"build",[469,4298,921],{"class":489},[469,4300,4301],{"class":471,"line":1193},[469,4302,679],{"class":507},[469,4304,4305],{"class":471,"line":1198},[469,4306,525],{"emptyLinePlaceholder":320},[469,4308,4309,4311,4313,4316,4318,4320],{"class":471,"line":1203},[469,4310,644],{"class":475},[469,4312,779],{"class":475},[469,4314,4315],{"class":660}," notifyDistance",[469,4317,664],{"class":507},[469,4319,4096],{"class":475},[469,4321,4099],{"class":507},[469,4323,4324,4326,4329,4331,4333,4335,4338],{"class":471,"line":1210},[469,4325,3296],{"class":479},[469,4327,4328],{"class":507}," n ",[469,4330,512],{"class":511},[469,4332,2219],{"class":479},[469,4334,31],{"class":489},[469,4336,4337],{"class":660},"createNotification",[469,4339,4340],{"class":489},"(distance);\n",[469,4342,4343,4346,4348,4350,4352,4354,4356,4358,4360,4362,4364],{"class":471,"line":1221},[469,4344,4345],{"class":479},"        NotificationManager",[469,4347,4038],{"class":507},[469,4349,512],{"class":511},[469,4351,3931],{"class":479},[469,4353,31],{"class":489},[469,4355,3146],{"class":660},[469,4357,664],{"class":489},[469,4359,4051],{"class":479},[469,4361,31],{"class":489},[469,4363,476],{"class":479},[469,4365,674],{"class":489},[469,4367,4368,4371,4373,4376],{"class":471,"line":1232},[469,4369,4370],{"class":479},"        notificationManager",[469,4372,31],{"class":489},[469,4374,4375],{"class":660},"notify",[469,4377,4378],{"class":489},"(ARROUND_ID, n);\n",[469,4380,4381],{"class":471,"line":1242},[469,4382,679],{"class":507},[12,4384,4385],{},"Il est important lors de l'appel à notifyDistance de toujours utiliser le même identifiant de notification afin\nque cette dernière soit remplacée (et non ajouté). Cela permet de mettre à jour le contenu de la notification.",[33,4387,4389],{"id":4388},"pour-finir","Pour finir",[12,4391,4392],{},"Pour finir je suis content d'avoir développé cette application qui n'est pas exempte de bug, mais qui m'a pris\ntrès peu de temps de développement.",[12,4394,4395,4396,4401,4402,4407],{},"Vous pouvez retrouver le code source sur ",[47,4397,4400],{"href":4398,"rel":4399},"https:\u002F\u002Fgithub.com\u002Fphoenix741\u002F1kmarround",[51],"Github: phoenix741\u002F1kmarround","\net sur le ",[47,4403,4406],{"href":4404,"rel":4405},"https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=org.shadoware.a1kmarroud",[51],"PlayStore"," (à ce jour l'application\nn'a pas encore été validé dans les stores et n'est donc pas disponible).",[12,4409,4410],{},"Le plus long dans ce développement aura été:",[140,4412,4413,4421,4424],{},[143,4414,4415,4416,1387],{},"faire l'icône (à partir d'une image se trouvant sur ",[47,4417,4420],{"href":4418,"rel":4419},"https:\u002F\u002Fundraw.co\u002F",[51],"undraw.co",[143,4422,4423],{},"faire le layout (même si elle est simple)",[143,4425,4426],{},"remplir la fiche du playstore pour mettre l'application dans les stores.",[4428,4429,4432,4437],"section",{"className":4430,"dataFootnotes":291},[4431],"footnotes",[33,4433,4436],{"className":4434,"id":422},[4435],"sr-only","Footnotes",[4438,4439,4440,4451],"ol",{},[143,4441,4443,4444],{"id":4442},"user-content-fn-1","Une activité est l'équivalent d'un écran dans Android. ",[47,4445,4450],{"href":4446,"ariaLabel":4447,"className":4448,"dataFootnoteBackref":291},"#user-content-fnref-1","Back to reference 1",[4449],"data-footnote-backref","↩",[143,4452,4454,4455],{"id":4453},"user-content-fn-2","Pour demander les permissions je me suis basé sur le code suivant: ",[47,4456,4450],{"href":4457,"ariaLabel":4458,"className":4459,"dataFootnoteBackref":291},"#user-content-fnref-2","Back to reference 2",[4449],[4461,4462,4463],"style",{},"html pre.shiki code .seHd6, html code.shiki .seHd6{--shiki-default:#C678DD}html pre.shiki code .sU0A5, html code.shiki .sU0A5{--shiki-default:#E5C07B}html pre.shiki code .sn6KH, html code.shiki .sn6KH{--shiki-default:#ABB2BF}html pre.shiki code .sVyAn, html code.shiki .sVyAn{--shiki-default:#E06C75}html pre.shiki code .sjrmR, html code.shiki .sjrmR{--shiki-default:#56B6C2}html pre.shiki code .subq3, html code.shiki .subq3{--shiki-default:#98C379}html pre.shiki code .sVC51, html code.shiki .sVC51{--shiki-default:#D19A66}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sVbv2, html code.shiki .sVbv2{--shiki-default:#61AFEF}html pre.shiki code .s_ZVi, html code.shiki .s_ZVi{--shiki-default:#E06C75;--shiki-default-font-style:italic}html pre.shiki code .sV9Aq, html code.shiki .sV9Aq{--shiki-default:#7F848E;--shiki-default-font-style:italic}",{"title":291,"searchDepth":292,"depth":292,"links":4465},[4466,4467,4471,4472,4473],{"id":386,"depth":292,"text":387},{"id":434,"depth":292,"text":414,"children":4468},[4469,4470],{"id":451,"depth":522,"text":452},{"id":1674,"depth":522,"text":1675},{"id":2731,"depth":292,"text":2732},{"id":4388,"depth":292,"text":4389},{"id":422,"depth":292,"text":4436},"2020-11-14",{"type":9,"value":4476},[4477,4479,4481,4486,4491,4493,4495,4497],[12,4478,340],{},[12,4480,343],{},[12,4482,346,4483,352],{},[47,4484,351],{"href":349,"rel":4485},[51],[12,4487,355,4488,360],{},[47,4489,351],{"href":358,"rel":4490},[51],[12,4492,363],{},[12,4494,366],{},[12,4496,369],{},[140,4498,4499,4501,4503,4505],{},[143,4500,374],{},[143,4502,377],{},[143,4504,380],{},[143,4506,383],{},{"planet":320},"\u002Fpost\u002Fapplication_du_confinement",{"title":335,"description":340},"application_du_confinement","posts\u002FProgrammation\u002F2020-11-14_application_du_confinement",[330,301,464,4513],"confinement","H4G4WeTzcsmG6glUDJrc_7H09bZb4hoavg3yFXRDN3A",1777849582783]